initial
This commit is contained in:
535
LiamMsGraph/cMsGraphBase.cs.bak
Normal file
535
LiamMsGraph/cMsGraphBase.cs.bak
Normal file
@@ -0,0 +1,535 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using C4IT.Logging;
|
||||
|
||||
|
||||
namespace C4IT.MsGraph
|
||||
{
|
||||
public class cMsGraphBase
|
||||
{
|
||||
public enum eHttpMethod { get, post, put, delete};
|
||||
|
||||
public const string constAzureLoginUrl = "https://login.microsoftonline.com/{Tenant}/oauth2/v2.0/token";
|
||||
public const string constAzureAuthorizeUrl = "https://login.microsoftonline.com/{Tenant}/oauth2/v2.0/authorize";
|
||||
public const string constMsGraphUrl = "https://graph.microsoft.com/{Version}/{Request}";
|
||||
public const string constMsGraphApplicationGet = "applications?$filter=appId eq '{AppID}'";
|
||||
public const string constMsGraphProfileGet = "me";
|
||||
|
||||
public bool IsOnline { get; private set; } = false;
|
||||
|
||||
public string AccessToken { get; private set; } = null;
|
||||
public DateTime TokenExpiresIn { get; private set; } = DateTime.MinValue;
|
||||
|
||||
private cMsGraphLogonInfo privLogonInfo = null;
|
||||
|
||||
public Exception LastException { get; private set; } = null;
|
||||
public string LastErrorMessage { get; private set; } = null;
|
||||
|
||||
public cMsGraphResultApplication Me { get; private set; } = null;
|
||||
|
||||
public HttpStatusCode? LastHttpErrorCode { get; private set; } = null;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ResetError()
|
||||
{
|
||||
LastException = null;
|
||||
LastErrorMessage = null;
|
||||
LastHttpErrorCode = null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetErrorException(string Action, Exception E, LogLevels lev = LogLevels.Error)
|
||||
{
|
||||
LastException = E;
|
||||
LastErrorMessage = Action + ": " + E.Message;
|
||||
cLogManager.LogEntry(Action, lev);
|
||||
}
|
||||
|
||||
private async Task<bool> privLogonAsync(cMsGraphLogonInfo LogonInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
ResetError();
|
||||
IsOnline = false;
|
||||
|
||||
// prepare the http login request
|
||||
var postData = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>("client_id", LogonInfo.ClientID),
|
||||
new KeyValuePair<string, string>("scope", LogonInfo.Scope),
|
||||
new KeyValuePair<string, string>("redirect_uri", $"https://login.microsoftonline.com/{LogonInfo.Tenant}/oauth2/nativeclient"),
|
||||
new KeyValuePair<string, string>("grant_type", "client_credentials"),
|
||||
new KeyValuePair<string, string>("client_secret", LogonInfo.ClientSecret)
|
||||
};
|
||||
|
||||
var formData = new FormUrlEncodedContent(postData);
|
||||
var strUrl = constAzureLoginUrl.Replace("{Tenant}", LogonInfo.Tenant);
|
||||
var request = new HttpRequestMessage(System.Net.Http.HttpMethod.Post, strUrl)
|
||||
{
|
||||
Content = formData
|
||||
};
|
||||
|
||||
|
||||
// do the http login request
|
||||
using (var httpClient = new HttpClient())
|
||||
{
|
||||
var response = await httpClient.SendAsync(request);
|
||||
LastHttpErrorCode = response.StatusCode;
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
LastErrorMessage = $"HTTP request error {response.StatusCode} while Azure tenant login: {response.ReasonPhrase}";
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the response content & the access token
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
dynamic iToken = JsonConvert.DeserializeObject(content);
|
||||
AccessToken = iToken.access_token;
|
||||
int ExpiresIn = iToken.expires_in;
|
||||
TokenExpiresIn = DateTime.UtcNow + TimeSpan.FromSeconds(ExpiresIn * 0.7);
|
||||
var TokenType = iToken.token_type;
|
||||
if (TokenType != "Bearer")
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AccessToken != null)
|
||||
{
|
||||
IsOnline = true;
|
||||
Me = await RequestApplicationAsync(LogonInfo.ClientID);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
SetErrorException("HTTP exception error while Azure tenant login", E, LogLevels.Debug);
|
||||
cLogManager.LogException(E, LogLevels.Debug);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<bool> privRelogon()
|
||||
{
|
||||
if (DateTime.UtcNow < TokenExpiresIn)
|
||||
return true;
|
||||
if (privLogonInfo == null)
|
||||
return false;
|
||||
var RetVal = await privLogonAsync(privLogonInfo);
|
||||
return RetVal;
|
||||
}
|
||||
|
||||
public async Task<bool> LogonAsync(cMsGraphLogonInfo LogonInfo)
|
||||
{
|
||||
var RetVal = await privLogonAsync(LogonInfo);
|
||||
if (RetVal == true)
|
||||
privLogonInfo = LogonInfo;
|
||||
return RetVal;
|
||||
}
|
||||
|
||||
private async Task<dynamic> privRequestAsync(string Url, eHttpMethod httpMethod = eHttpMethod.get, object JsonData = null, bool retryForbidden = false)
|
||||
{
|
||||
ResetError();
|
||||
try
|
||||
{
|
||||
await RequestCriticalSection.Semaphore.WaitAsync();
|
||||
|
||||
if (!IsOnline)
|
||||
return null;
|
||||
|
||||
if (!await privRelogon())
|
||||
return null;
|
||||
|
||||
var retryCount = 0;
|
||||
while (true)
|
||||
{
|
||||
// create the request
|
||||
using (var handler = new HttpClientHandler())
|
||||
{
|
||||
using (var client = new HttpClient(handler))
|
||||
{
|
||||
client.DefaultRequestHeaders.Accept.Clear();
|
||||
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
client.DefaultRequestHeaders.Authorization =
|
||||
new AuthenticationHeaderValue("Bearer", AccessToken);
|
||||
HttpResponseMessage response;
|
||||
StringContent CO = null;
|
||||
if (JsonData != null)
|
||||
{
|
||||
var strJson = JsonConvert.SerializeObject(JsonData);
|
||||
CO = new StringContent(strJson, Encoding.UTF8, "application/json");
|
||||
}
|
||||
switch (httpMethod)
|
||||
{
|
||||
case eHttpMethod.get:
|
||||
response = await client.GetAsync(Url);
|
||||
break;
|
||||
case eHttpMethod.post:
|
||||
response = await client.PostAsync(Url, CO);
|
||||
break;
|
||||
case eHttpMethod.delete:
|
||||
response = await client.DeleteAsync(Url);
|
||||
break;
|
||||
case eHttpMethod.put:
|
||||
response = await client.PutAsync(Url, CO);
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
LastHttpErrorCode = response.StatusCode;
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
if (retryForbidden && (LastHttpErrorCode == HttpStatusCode.Forbidden) && (retryCount < 2))
|
||||
{
|
||||
retryCount++;
|
||||
await (Task.Delay(100));
|
||||
}
|
||||
else
|
||||
{
|
||||
LastErrorMessage = $"HTTP request error {(int)response.StatusCode} while MS Graph request '{Url}': {response.ReasonPhrase}";
|
||||
if (cLogManager.DefaultLogger.IsDebug)
|
||||
cLogManager.DefaultLogger.LogEntry(LogLevels.Warning, LastErrorMessage);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (response.StatusCode == HttpStatusCode.NoContent)
|
||||
return true;
|
||||
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
dynamic jsonData = JsonConvert.DeserializeObject(content); ;
|
||||
|
||||
return jsonData;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
SetErrorException("HTTP exception error while while MS Graph request '{Request}'", E);
|
||||
cLogManager.LogException(E);
|
||||
}
|
||||
finally
|
||||
{
|
||||
RequestCriticalSection.Semaphore.Release();
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<cMsGraphResultBase> RequestAsync(string Request, bool UseBeta = false, eHttpMethod httpMethod = eHttpMethod.get, object JsonData = null, bool retryForbidden = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// get the request url
|
||||
var strVersion = "v1.0";
|
||||
if (UseBeta)
|
||||
strVersion = "beta";
|
||||
var strUrl = constMsGraphUrl.Replace("{Version}", strVersion);
|
||||
strUrl = strUrl.Replace("{Request}", Request);
|
||||
|
||||
var data = await privRequestAsync(strUrl, httpMethod, JsonData, retryForbidden);
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
if (data is bool isValid)
|
||||
{
|
||||
if (isValid)
|
||||
return new cMsGraphResultBase(null);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
var RetVal = new cMsGraphResultBase(data);
|
||||
return RetVal;
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
cLogManager.LogException(E);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<cMsGraphResultList> RequestListAsync(string Request, bool UseBeta = false, bool loadPaged = false, bool retryForbidden = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// get the request url
|
||||
var strVersion = "v1.0";
|
||||
if (UseBeta)
|
||||
strVersion = "beta";
|
||||
var strUrl = constMsGraphUrl.Replace("{Version}", strVersion);
|
||||
strUrl = strUrl.Replace("{Request}", Request);
|
||||
|
||||
var res = await privRequestAsync(strUrl, retryForbidden: retryForbidden);
|
||||
if (res != null)
|
||||
{
|
||||
var RetVal = new cMsGraphResultList(retryForbidden);
|
||||
RetVal.AddResult(res);
|
||||
if (!RetVal.hasRemaining || loadPaged)
|
||||
return RetVal;
|
||||
while (RetVal.hasRemaining)
|
||||
{
|
||||
if (!await RequestNextAsync(RetVal))
|
||||
return RetVal;
|
||||
}
|
||||
return RetVal;
|
||||
}
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
cLogManager.DefaultLogger.LogException(E);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<bool> RequestNextAsync(cMsGraphResultList List)
|
||||
{
|
||||
try
|
||||
{
|
||||
var res = await privRequestAsync(List.NextResultUrl, retryForbidden: List.retryForbidden);
|
||||
if (res != null)
|
||||
{
|
||||
List.AddResult(res);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
cLogManager.DefaultLogger.LogException(E);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<cMsGraphResultUser> RequestProfileAsync()
|
||||
{
|
||||
ResetError();
|
||||
try
|
||||
{
|
||||
var Result = await RequestAsync(constMsGraphProfileGet);
|
||||
if (Result != null)
|
||||
return new cMsGraphResultUser(Result);
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
cLogManager.LogException(E);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<cMsGraphResultApplication> RequestApplicationAsync(string AppID)
|
||||
{
|
||||
ResetError();
|
||||
try
|
||||
{
|
||||
var strRequest = constMsGraphApplicationGet.Replace("{AppID}", AppID);
|
||||
var Result = await RequestAsync(strRequest, retryForbidden: true);
|
||||
if (Result != null)
|
||||
{
|
||||
dynamic lst = Result.Result.value;
|
||||
if (Result.Result.value.Count >= 1)
|
||||
{
|
||||
dynamic app = Result.Result.value[0];
|
||||
var RetVal = new cMsGraphResultBase(app);
|
||||
return new cMsGraphResultApplication(RetVal);
|
||||
}
|
||||
|
||||
return new cMsGraphResultApplication(Result);
|
||||
}
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
cLogManager.LogException(E);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class cMsGraphLogonInfo
|
||||
{
|
||||
public string Tenant;
|
||||
public string ClientID;
|
||||
public string ClientSecret;
|
||||
public string Scope = "https://graph.microsoft.com/.default";
|
||||
}
|
||||
|
||||
public static class RequestCriticalSection
|
||||
{
|
||||
public static SemaphoreSlim Semaphore = new SemaphoreSlim(1, 1);
|
||||
}
|
||||
|
||||
public class cMsGraphResultBase
|
||||
{
|
||||
public string ID { get; private set; } = null;
|
||||
public string ODataId { get; private set; } = null;
|
||||
public string DisplayName { get; private set; } = null;
|
||||
|
||||
public string Context { get; private set; } = null;
|
||||
|
||||
public dynamic Result { get; private set; } = null;
|
||||
|
||||
public cMsGraphResultBase(dynamic Result)
|
||||
{
|
||||
this.Result = Result;
|
||||
|
||||
try { ID = Result.id; } catch { };
|
||||
try {
|
||||
if (Result.TryGetValue("displayName", out JToken JT1))
|
||||
DisplayName = Result.displayName;
|
||||
else if (Result.TryGetValue("name", out JToken JT2))
|
||||
DisplayName = Result.name;
|
||||
}
|
||||
catch { }
|
||||
try { Context = Result["@odata.context"]; } catch { }
|
||||
ODataId = GetStringFromDynamic(Result, "@odata.id");
|
||||
if (string.IsNullOrEmpty(ODataId))
|
||||
ODataId = @"https://graph.microsoft.com/v1.0/directoryObjects/" + ID;
|
||||
}
|
||||
|
||||
public cMsGraphResultBase(cMsGraphResultBase Result)
|
||||
{
|
||||
if (Result == null)
|
||||
return;
|
||||
|
||||
this.Result = Result.Result;
|
||||
ID = Result.ID;
|
||||
ODataId = Result.ODataId;
|
||||
DisplayName = Result.DisplayName;
|
||||
Context = Result.Context;
|
||||
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string GetStringFromDynamic(dynamic O, string ProperyName)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (string)O[ProperyName];
|
||||
}
|
||||
catch { }
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class cMsGraphResultList : List<cMsGraphResultBase>
|
||||
{
|
||||
public string NextResultUrl { get; private set; } = null;
|
||||
public bool retryForbidden { get; private set; } = false;
|
||||
|
||||
public cMsGraphResultList(bool retryForbidden)
|
||||
{
|
||||
this.retryForbidden = retryForbidden;
|
||||
}
|
||||
|
||||
public bool hasRemaining
|
||||
{
|
||||
get
|
||||
{
|
||||
return !string.IsNullOrEmpty(NextResultUrl);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddResult(dynamic Result)
|
||||
{
|
||||
try
|
||||
{
|
||||
NextResultUrl = null;
|
||||
|
||||
if (Result.TryGetValue("@odata.nextLink", out JToken JT))
|
||||
{
|
||||
var JO = (JValue)JT;
|
||||
|
||||
NextResultUrl = Result["@odata.nextLink"];
|
||||
}
|
||||
|
||||
if (Result.TryGetValue("value", out JToken JT2))
|
||||
{
|
||||
foreach (dynamic Entry in Result.value)
|
||||
try
|
||||
{
|
||||
var val = new cMsGraphResultBase(Entry);
|
||||
this.Add(val);
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
cLogManager.DefaultLogger.LogException(E);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
cLogManager.DefaultLogger.LogException(E);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class cMsGraphResultUser : cMsGraphResultBase
|
||||
{
|
||||
public string UserPrincipalName { get; private set; } = null;
|
||||
public string EMail { get; private set; } = null;
|
||||
public string GivenName { get; private set; } = null;
|
||||
public string SurName { get; private set; } = null;
|
||||
public string JobTitle { get; private set; } = null;
|
||||
public string OfficeLocation { get; private set; } = null;
|
||||
public string PreferredLanguage { get; private set; } = null;
|
||||
|
||||
public cMsGraphResultUser(cMsGraphResultBase Result) : base(Result)
|
||||
{
|
||||
UserPrincipalName = GetStringFromDynamic(Result.Result, "userPrincipalName");
|
||||
EMail = GetStringFromDynamic(Result.Result, "mail");
|
||||
GivenName = GetStringFromDynamic(Result.Result, "givenName");
|
||||
SurName = GetStringFromDynamic(Result.Result, "surname");
|
||||
JobTitle = GetStringFromDynamic(Result.Result, "jobTitle");
|
||||
OfficeLocation = GetStringFromDynamic(Result.Result, "officeLocation");
|
||||
PreferredLanguage = GetStringFromDynamic(Result.Result, "preferredLanguage");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class cMsGraphCollectionUsers : Dictionary<string, cMsGraphResultUser>
|
||||
{
|
||||
public cMsGraphCollectionUsers() { }
|
||||
public cMsGraphCollectionUsers(int n) : base(n) { }
|
||||
|
||||
public void Add(cMsGraphResultUser User)
|
||||
{
|
||||
if (!this.ContainsKey(User.ID))
|
||||
this.Add(User.ID, User);
|
||||
}
|
||||
}
|
||||
|
||||
public class cMsGraphResultApplication : cMsGraphResultBase
|
||||
{
|
||||
public string AppID { get; private set; } = null;
|
||||
public string PublisherDomain { get; private set; } = null;
|
||||
|
||||
public cMsGraphResultApplication(cMsGraphResultBase Result) : base(Result)
|
||||
{
|
||||
try { AppID = Result.Result.appId; } catch { };
|
||||
try { PublisherDomain = Result.Result.publisherDomain; } catch { };
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user