How to update UserProfile Properties on Sharepoint Online 2013 (O365) - c#

I've been trying to update a user profile properties using c# on Sharepoint Online 2013.
I can't find how to do it, can someone help me?
Here is what i have to do:
I have a lot of custom properties on User Profile, and i need to edit it on an Provider-Hosted app.
I'm using PersonProperties and PeopleManager to get the data, so how to update that?
I appreciate your help!

This will probably be of some help
Using the UserProfileService, this class should help with your issue
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using Microsoft.SharePoint.Client;
using O365ProfileUpdate.UserProfileServiceRef;
public class O365Helper
{
private readonly UserProfileService _userProfileService;
private readonly Uri _targetAdminSite;
public O365Helper(UserProfileService userProfileService, Uri targetAdminSite, string adminUsername,
string adminPassword)
{
_userProfileService = userProfileService;
_targetAdminSite = targetAdminSite;
var authenticated = AuthenticateAdministrator(adminUsername, adminPassword);
if (!authenticated)
throw new UnauthorizedAccessException("Unable to authenticate administrator");
}
public PropertyData GetProfileProperty(string login, string propertyName)
{
var memLogin = GetMembershipLogin(login);
return _userProfileService.GetUserPropertyByAccountName(memLogin, propertyName);
}
public bool UpdateProfileProperty(string login, string key, string value)
{
try
{
var valueData = new ValueData {Value = value};
var newdata = new PropertyData[1];
newdata[0] = new PropertyData {Name = key, Values = new ValueData[1]};
newdata[0].Values[0] = valueData;
newdata[0].IsValueChanged = true;
var memLogin = GetMembershipLogin(login);
_userProfileService.ModifyUserPropertyByAccountName(memLogin, newdata);
}
catch
{
return false;
}
return true;
}
private bool AuthenticateAdministrator(string login, string password)
{
try
{
var securePassword = new SecureString();
foreach (char c in password)
{
securePassword.AppendChar(c);
}
var onlineCredentials = new SharePointOnlineCredentials(login, securePassword);
string authCookieValue = onlineCredentials.GetAuthenticationCookie(_targetAdminSite);
var cookieVal = authCookieValue.TrimStart("SPOIDCRL=".ToCharArray());
_userProfileService.CookieContainer = new CookieContainer();
_userProfileService.CookieContainer.Add(new Cookie(
"FedAuth",
cookieVal,
String.Empty,
_targetAdminSite.Authority));
}
catch
{
return false;
}
return true;
}
private string GetMembershipLogin(string login)
{
return "i:0#.f|membership|" + login;
}
}
adminUsername and adminPassword are the credentials for a user with administrative privileges (so, probably you) in your instance
The UserProfileService can be found in the UserProfileService.asmx endpoint in your O365 ADMIN site

Related

Need assistance in reading authorization code from URL in C# Windows based application

I am trying to implement oAuth2 in a Windows based application using C# (It is very straight forward from a web application).
And I am struggling to read the code from return/redirected URL
Below are the sequence of steps
Get authorization code by hitting the URL
https://{Server}/auth/oauth2/authorize?response_type=code&client_id=_ClientId}&scope=user&redirect_uri=http://localhost&state=123456789
Note: When we hit above URL it internally re-directs to on-prem ADFS server (something like https://adfsserver/...) sends back the SAML Token to {Server} which validates the token and generates Authorization code in following format
http://localhost/?code=JSgTYUHfrIO6pHA8ha5Z55MDuC8bEl1K&state=123456789
Now I need to read the code value from above URL.
I tried to use WebBrowser control in C# but unfortunately it captures initial re-direct occurring to our ADFS server i.e. it is capturing URL https://adfsserver/....
Can anyone please guide me on how to capture the Code from destination URL?
Note: Again I am trying to achieve this from Console/Windows/WPF application.
Thank you in advance.
Used WebBrowser control but not successful
this._uri = $"https://{_Server}/auth/oauth2/authorize?response_type=code&client_id={_ClientId}&scope=user&redirect_uri=http://localhost&state=123456789";
this._browser = new System.Windows.Forms.WebBrowser();
this._browser.Navigated += new WebBrowserNavigatedEventHandler(browser_Navigated);
this._browser.Navigate(this._uri);
This is how I achieved it
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Web;
using System.Windows.Forms;
namespace OAuthDMS.App_Code.RestApi
{
internal class Authorization_Code
{
private static string _AuthCode = "";
private static string _State = "";
private static WebBrowser _WebBrowser = null;
internal void AuthorizationCodeLogin()
{
// Force TLS 1.2 instead of the default value.
ServicePointManager.ServerCertificateValidationCallback = (s, cert, chain, ssl) => true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//Auth code url
string _AuthCodeUri = $"https://{Properties.Server}/auth/oauth2/authorize?response_type=code&client_id={Properties.ClientId}&scope=user&redirect_uri=http://localhost&state=123456789";
//first get authorization code
_WebBrowser = new WebBrowser();
_WebBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(GetAuthorizationCode);
_WebBrowser.Url = new Uri(_AuthCodeUri);
}
private void GetAuthorizationCode(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (e.Url.AbsolutePath.Equals("/"))
{
_AuthCode = HttpUtility.ParseQueryString(e.Url.Query).Get("code");
_State = HttpUtility.ParseQueryString(e.Url.Query).Get("state");
if (_AuthCode != null && _State == "123456789")
{
OAuth2();
}
}
}
private async void OAuth2()
{
//login url
string _OAuth2Uri = $"https://{Properties.Server}/auth/oauth2/token";
//Holds authentication token details (on success)
AuthenticationToken _WorkAuthToken = null;
//Authenticating into DMS using password grant
Dictionary<string, string> _RequestBody = new()
{
{ "grant_type", "authorization_code" },
{ "client_id", Properties.ClientId },
{ "client_secret", Properties.ClientSecret },
{ "code", _AuthCode },
{ "redirect_uri", "http://localhost" }
};
using (HttpClient _HttpClient = new())
{
using (HttpRequestMessage _HttpReqMsg = new(HttpMethod.Post, _OAuth2Uri))
{
_HttpReqMsg.Content = new FormUrlEncodedContent(_RequestBody);
using (HttpResponseMessage _HttpResMsg = await _HttpClient.SendAsync(_HttpReqMsg))
{
if (_HttpResMsg.StatusCode == HttpStatusCode.OK)
{
_WorkAuthToken = JsonSerializer.Deserialize<AuthenticationToken>(_HttpResMsg.Content.ReadAsStringAsync().Result);
}
}
}
}
MessageBox.Show(_WorkAuthToken.access_token);
}
public class AuthenticationToken
{
public string access_token { get; set; }
public string token_type { get; set; }
public string scope { get; set; }
public string refresh_token { get; set; }
public int expires_in { get; set; }
}
}
}

HTTP status code 500 when querying an API from a C# program, except the API works

I need to use another company's API to query data using POST requests.
The API works (= I receive all the data with no errors) when I query it from the swagger website using the UI, but when I do it from my C# program I get a 500 Internal Server Error.
Where should I be looking for the problem ? Is there a way to get a more detailed error message ?
Edit (added code) :
using System;
using System.Data.Entity;
using System.Data.Entity.Core.Mapping;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Infrastructure.Interception;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.Serialization;
using System.Text;
namespace credex_distribution_offers_to_interfaces
{
class Program
{
private const string jsonMediaType = "application/json";
static void Main()
{
FetchJSONAndInsertToDB();
}
private static bool FetchJSONAndInsertToDB()
{
var baseServiceUrl = new Uri("a valid url");
Root rootObject;
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(jsonMediaType));
try
{
string token = FetchToken(httpClient, baseServiceUrl);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(token);
}
catch (Exception e)
{
return false;
}
try
{
rootObject = FetchDistributionLookupOffers(httpClient, baseServiceUrl, 29612, 29613, 29614, 29617, 29621);
}
catch (Exception e)
{
return false;
}
}
// database related stuff...
// ...
return true;
}
[DataContract]
public class MortgageForDistributionLookupInputDto
{
public int[] OfferNumbers { get; set; }
}
private static Root FetchDistributionLookupOffers(HttpClient aHttpClient, Uri aBaseServiceUrl, params int[] aOfferNumbers)
{
var input = new MortgageForDistributionLookupInputDto()
{
OfferNumbers = aOfferNumbers
};
var lookup = aHttpClient.PostAsync(new Uri(aBaseServiceUrl, "v1/MortgageDetails/InvestorLookupOffers"), PayloadFor(input)).Result;
if (lookup.StatusCode != HttpStatusCode.OK)
{
throw new Exception("Fetching investor lookup offers failed with HTTP status code '" + lookup.StatusCode + "' : " + lookup.ReasonPhrase + "}");
}
var obj = ValueFor<Root>(lookup.Content);
return obj;
}
private static HttpContent PayloadFor<T>(T aObject)
{
return new StringContent(aObject.SerializeJson(), Encoding.UTF8, jsonMediaType);
}
private static T ValueFor<T>(HttpContent aHttpContent)
{
//var content = aHttpContent.ReadAsStringAsync();
return aHttpContent.ReadAsStreamAsync().Result.DeSerializeJson<T>();
}
private static string FetchToken(HttpClient aHttpClient, Uri aBaseServiceUrl)
{
var login = new LoginRequest()
{
UserName = "some user name",
Password = "some password"
};
var authResult = aHttpClient.PostAsync(new Uri(aBaseServiceUrl, "api/Login"), PayloadFor(login)).Result;
if (authResult.StatusCode != HttpStatusCode.OK)
{
throw new Exception("Fetching authentication token failed. Reason : " + authResult.StatusCode + " -> " + authResult.ReasonPhrase);
}
return authResult.Content.ReadAsStringAsync().Result.Trim('"');
}
}
}

Upload File to OneDrive using ASP.Net Web Forms

I have created a simple ASP.Net web forms application and want to upload a file to One Drive. I implemented MS Graph APIs for this purpose. There are three files, Upload.aspx, Upload.aspx.cs, and MsalAuthentication.cs (The code is also given below). When I click on "Upload button" and the control goes to:
var result = await _clientApplication.AcquireTokenByUsernamePassword(_scopes, _username, _password).ExecuteAsync();, it stucks here and doesn't move to the next statement.
In web.Config file, I have also given applicationId and tenantId as:
< appSettings >
< add key="tenantId" value="some id" />
< add key="applicationId" value="some id" />
< /appSettings>
Can anybody tell me about the issue?
The code is given below
Upload.aspx.cs
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using System.Security;
using Microsoft.Identity.Client;
using Microsoft.Graph;
using Microsoft.Extensions.Configuration;
using Helpers;
using System.Configuration;
using System.Collections.Specialized;
namespace WebFormsOneDrive
{
public partial class Upload : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
var config = LoadAppSettings();
if (config == null)
{
Console.WriteLine("Invalid appsettings.json file.");
return;
}
var userName = ReadUsername();
var userPassword = ReadPassword();
var client = GetAuthenticatedGraphClient(config, userName, userPassword);
// request 1 - upload small file to user's onedrive
var fileName = FileUpload1.FileName;
var filePath = Path.Combine(#"D:\webform\upload\", fileName);
FileStream fileStream = new FileStream(filePath, FileMode.Open);
var uploadedFile = client.Me.Drive.Root
.ItemWithPath(fileName)
.Content
.Request()
.PutAsync<DriveItem>(fileStream)
.Result;
Console.WriteLine("File uploaded to: " + uploadedFile.WebUrl);
}
private static NameValueCollection LoadAppSettings()
{
try
{
//var config = new ConfigurationBuilder()
// .SetBasePath(System.IO.Directory.GetCurrentDirectory())
// .AddXMLFile("Web.config", false, true)
// .Build();
var config = ConfigurationManager.GetSection("appSettings") as NameValueCollection;
if (string.IsNullOrEmpty(config["applicationId"]) ||
string.IsNullOrEmpty(config["tenantId"]))
{
return null;
}
return config;
}
catch (System.IO.FileNotFoundException)
{
return null;
}
}
private static IAuthenticationProvider CreateAuthorizationProvider(NameValueCollection config, string userName, SecureString userPassword)
{
var clientId = config["applicationId"];
var authority = $"https://login.microsoftonline.com/{config["tenantId"]}/v2.0";
List<string> scopes = new List<string>();
scopes.Add("User.Read");
scopes.Add("Files.Read");
scopes.Add("Files.ReadWrite");
var cca = PublicClientApplicationBuilder.Create(clientId)
.WithAuthority(authority)
.Build();
return MsalAuthenticationProvider.GetInstance(cca, scopes.ToArray(), userName, userPassword);
}
private static GraphServiceClient GetAuthenticatedGraphClient(NameValueCollection config, string userName, SecureString userPassword)
{
var authenticationProvider = CreateAuthorizationProvider(config, userName, userPassword);
var graphClient = new GraphServiceClient(authenticationProvider);
return graphClient;
}
private static SecureString ReadPassword()
{
//Console.WriteLine("Enter your password");
//SecureString password = new SecureString();
//while (true)
//{
// ConsoleKeyInfo c = Console.ReadKey(true);
// if (c.Key == ConsoleKey.Enter)
// {
// break;
// }
// password.AppendChar(c.KeyChar);
// Console.Write("*");
//}
//Console.WriteLine();
var password = new SecureString();
password.AppendChar('p');
password.AppendChar('a');
password.AppendChar('s');
password.AppendChar('s');
password.AppendChar('w');
password.AppendChar('o');
password.AppendChar('r');
password.AppendChar('d');
return password;
}
private static string ReadUsername()
{
//string username;
//Console.WriteLine("Enter your username");
//username = Console.ReadLine();
//return username;
string userName = "abcd#domain#onmicrosoft.com";
return userName;
}
}
}
MsalAuthentication.cs
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
using Microsoft.Graph;
namespace Helpers
{
public class MsalAuthenticationProvider : IAuthenticationProvider
{
private static MsalAuthenticationProvider _singleton;
private IPublicClientApplication _clientApplication;
private string[] _scopes;
private string _username;
private SecureString _password;
private string _userId;
private MsalAuthenticationProvider(IPublicClientApplication clientApplication, string[] scopes, string username, SecureString password)
{
_clientApplication = clientApplication;
_scopes = scopes;
_username = username;
_password = password;
_userId = null;
}
public static MsalAuthenticationProvider GetInstance(IPublicClientApplication clientApplication, string[] scopes, string username, SecureString password)
{
if (_singleton == null)
{
_singleton = new MsalAuthenticationProvider(clientApplication, scopes, username, password);
}
return _singleton;
}
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
var accessToken = await GetTokenAsync();
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
}
public async Task<string> GetTokenAsync()
{
if (!string.IsNullOrEmpty(_userId))
{
try
{
var account = await _clientApplication.GetAccountAsync(_userId);
if (account != null)
{
var silentResult = await _clientApplication.AcquireTokenSilent(_scopes, account).ExecuteAsync();
return silentResult.AccessToken;
}
}
catch (MsalUiRequiredException) { }
}
var result = await _clientApplication.AcquireTokenByUsernamePassword(_scopes, _username, _password).ExecuteAsync();
_userId = result.Account.HomeAccountId.Identifier;
return result.AccessToken;
}
I think you mixed 2 concepts:
access on behalf of a user's connection (via AAD for example)
access via the security of an application (client secret)
In general we cannot pass the password of a user in clear like that to an API, we play with tokens.
The Graph Doc for all scenarios
Add a client secret to your AAD and give all the roles base you need to your api.
If you use Credential flow, you should not use "Me" in the graph call, but something like : graph.Users["user#email.com"].Drive....
Otherwise if you realy want to use password you can do that :
IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.Build();
UsernamePasswordProvider authProvider = new UsernamePasswordProvider(publicClientApplication, scopes);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
User me = await graphClient.Me.Request()
.WithUsernamePassword(email, password)
.GetAsync();

C# Throwing unable to resolve controller error

I'm fairly new to .NET and c# and I'm working on a POC where I've run into an issue when a controller throws the error
System.InvalidOperation Exception {"Unable to resolve controller: TenantController"}
The Inner exception details are
No default Instance is registered and cannot be automatically determined for type 'GICS.Web.Managers.Interfaces.ITenantManager'
There is no configuration specified for GICS.Web.Managers.Interfaces.ITenantManager
1.) new TenantController(Default of ITenantManager, Default of IRemedyService)
2.) GICS.Web.Controllers.Api.TenantController
3.) Instance of GICS.Web.Controllers.Api.TenantController
4.) Container.GetInstance(GICS.Web.Controllers.Api.TenantController)
The TenantController looks as follows:
using System.Web.Mvc;
using GICS.Web.Controllers.Api.Abstracts;
using GICS.Web.Managers.Interfaces;
using GICS.Web.Services.Interfaces;
using System.Collections.Generic;
using GICS.Web.ViewModels.Tenant;
using GICS.Web.Models.Tenant;
namespace GICS.Web.Controllers.Api
{
[RoutePrefix("api/tenant")]
public class TenantController : BaseApiController
{
private readonly ITenantManager _tenantsManager;
private readonly IRemedyService _remedyService;
private string token;
public TenantController(ITenantManager tenantsManager, IRemedyService remedyService)
{
_tenantsManager = tenantsManager;
_remedyService = remedyService;
token = null;
}
[HttpGet, Route("{groupId}/{userName}")]
public JsonResult getTenants(string groupId, string UserName)
{
getToken(UserName);
JsonResult result = Json(null);
if (token != null)
{
var tenants = _tenantsManager.GetTenants(token, groupId);
List<TenantViewModel> tenantViewModelList = new List<TenantViewModel>();
foreach (Values x in tenants)
{
TenantViewModel model = new TenantViewModel(x, groupId);
tenantViewModelList.Add(model);
}
result = Json(tenantViewModelList);
}
return result;
}
}
The TenantManager interface is as follows:
using System.Collections.Generic;
using GICS.Web.Models.Tenant;
namespace GICS.Web.Managers.Interfaces
{
public interface ITenantManager
{
IEnumerable<Values> GetTenants(string token, string groupId);
}
}
And the Manager implementation is:
using GICS.Web.Managers.Abstracts;
using GICS.Web.Managers.Interfaces;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Configuration;
using System.Net;
using GICS.Web.Models.Tenant;
namespace GICS.Web.Managers
{
public class TentantManager : ManagerBase, ITenantManager
{
public IEnumerable<Models.Tenant.Values> GetTenants(string token, string groupId)
{
Tenant restEntries = null;
List<Models.Tenant.Values> tenantList = new List<Models.Tenant.Values>();
using (WebClient client = new WebClient())
{
client.Headers[HttpRequestHeader.Authorization] = token;
var baseURL = ConfigurationManager.AppSettings["RemedyBaseUrl"];
var apiPath = ConfigurationManager.AppSettings["RemedyAPIPath"];
string getURL = baseURL + apiPath + "ESN%3AAST%3ALogSysComp%5FASTPeople" + "?q=?q=%27PeopleGroup%20Form%20Entry%20ID%27%20%3D%20%22" + groupId + "%22&fields=values(Name)";
string getResponse = client.DownloadString(getURL);
restEntries = JsonConvert.DeserializeObject<Tenant>(getResponse);
foreach (Models.Tenant.Entry x in restEntries.entries)
{
tenantList.Add(x.values);
}
}
return tenantList;
}
}
}
I have other controllers in the project that follow the same approach and all are working except for this one. Anyone spot where I am going wrong here?
Thanks in advance.

Logging in Android App Using the data from database of ASP.net C#

I already used SOAP for getting the Idnumber from the database of ASp.net C# and I created an App that he/she can log using Email and Password and will go to next activity. This is the code that is used for trying to logged in using the Email and Password in WebSite.
public class MainActivity extends Activity {
EditText Email, Password;
Button button;
TextView error;
String EmailAddfromWeb, PassfromWeb;
private static final String SOAP_ACTION = "http://tempuri.org/findContact";
private static final String OPERATION_NAME = "CheckLogin";
private static final String WSDL_TARGET_NAMESPACE = "http://tempuri.org/";
private static final String SOAP_ADDRESS = "http://10.0.2.2:64485/WebService.asmx";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Email = (EditText)findViewById(R.id.editText1);
Password = (EditText)findViewById(R.id.editText2);
button = (Button)findViewById(R.id.button1);
error = (TextView)findViewById(R.id.textView3);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v){
SoapObject request = new SoapObject(WSDL_TARGET_NAMESPACE, OPERATION_NAME);
PropertyInfo propertyInfo = new PropertyInfo();
propertyInfo.type = PropertyInfo.STRING_CLASS;
propertyInfo.name = "EmailAdd";
EmailAddfromWeb = Email.getText().toString();
request.addProperty(propertyInfo, EmailAddfromWeb);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
HttpTransportSE httpTransport = new HttpTransportSE(SOAP_ADDRESS);
try
{
httpTransport.call(SOAP_ACTION, envelope);
Object response = envelope.getResponse();
error.setText(response.toString());
SPLPVIEWER s = new SPLPVIEWER();
Intent intent = new Intent(MainActivity.this, SPLPVIEWER.class);
Bundle b = new Bundle();
b.putString(s.holder, "" + String.valueOf(Email));
intent.putExtras(b);
startActivity(intent);
finish();
}
catch(Exception e)
{
error.setText("Invalid Email!");
}
}
});
Code for WebService.asmx.cs :
How can i compare my data login in Web Service from the inputted in AndroidApplication.
using System;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml.Linq;
using System.Data.SqlClient;
using System.Data;
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class WebService : System.Web.Services.WebService
{
public WebService()
{
}
[WebMethod]
public bool CheckLogin(string EmailAdd, string Password)
{
return getEmail(EmailAdd, Password);
}
public bool getEmail(String EmailAdd, String Password)
{
SqlConnection conn;
conn = ConnectionManager.GetConnection();
conn.Open();
bool check = false;
string pa;
SqlCommand CheckCmd = new SqlCommand("CheckEmailAddress", conn);
CheckCmd.CommandType = CommandType.StoredProcedure;
CheckCmd.Parameters.Add("#EmailAddress", SqlDbType.NVarChar).Value = EmailAdd;
SqlDataReader sdr = CheckCmd.ExecuteReader();
while (sdr.Read())
{
pa = sdr.GetString(8); // for Password column
if (pa == Password)
{
check = true;
}
break;
}
conn.Close();
return check;
}
}
use asp Web service or simple post methods.
Try this http://www.androidhive.info/2011/11/android-xml-parsing-tutorial/
Build your xml page via asp.net.

Categories