diff --git a/src/OneLoginClient/Converters/AppConverter.cs b/src/OneLoginClient/Converters/AppConverter.cs new file mode 100644 index 0000000..de97daa --- /dev/null +++ b/src/OneLoginClient/Converters/AppConverter.cs @@ -0,0 +1,46 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using OneLogin.Types; +using System; +using System.Reflection; + +namespace OneLogin.Converters +{ + public class AppConverter : JsonConverter where T : AppDetail, new() + { + public override bool CanConvert(Type objectType) + { + // ToDo: Build modern target framework without GetTypeInfo hack + return typeof(AppDetail).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo()); + } + + public override bool CanWrite => false; + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + + JObject jObject = JObject.Load(reader); + + AppConfiguration configTarget = null; + if (jObject.ContainsKey("auth_method")) + { + var authMethod = (int)jObject["auth_method"]; + switch (authMethod) + { + case 2: + configTarget = new AppSamlConfiguration(); + break; + } + } + + var target = new T() { Configuration = configTarget }; + serializer.Populate(jObject.CreateReader(), target); + return target; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/OneLoginClient/Endpoints.cs b/src/OneLoginClient/Endpoints.cs index 4c2f14f..b825a63 100644 --- a/src/OneLoginClient/Endpoints.cs +++ b/src/OneLoginClient/Endpoints.cs @@ -16,6 +16,11 @@ public static class Endpoints public const string BaseApi = OneLoginApiBase + API + ApiVersion; public const string BaseApi2 = OneLoginApiBase + API + ApiVersion2; + /// + /// https://api.{us_or_eu}.onelogin.com/api/2/apps + /// + public const string ONELOGIN_APPS = "apps"; + /// /// https://api..onelogin.com/api/1/users /// diff --git a/src/OneLoginClient/OneLogin.Client.xml b/src/OneLoginClient/OneLogin.Client.xml index 359f918..0832dc5 100644 --- a/src/OneLoginClient/OneLogin.Client.xml +++ b/src/OneLoginClient/OneLogin.Client.xml @@ -35,6 +35,11 @@ + + + https://api.{us_or_eu}.onelogin.com/api/2/apps + + @@ -88,6 +93,49 @@ + + + Utility method to call OneLogin GET API endpoints and deserialize response + + The type of the object to deserialize. + The path of the API endpoint + A dictionary of query string parameters + + + + + Utility method to call OneLogin GET API endpoints and deserialize response + + The type of the object to deserialize. + The relative URL (path and optional query string) of the API endpoint + + + + + Utility method to call OneLogin POST API endpoints and deserialize response + + The type of the object to deserialize. + The relative URL (path and optional query string) of the API endpoint + The object to serialize + + + + + Utility method to call OneLogin PUT API endpoints and deserialize response + + The type of the object to deserialize. + The relative URL (path and optional query string) of the API endpoint + The object to serialize + + + + + Utility method to call OneLogin DELETE API endpoints and deserialize response + + The type of the object to deserialize. + The relative URL (path and optional query string) of the API endpoint + + Create a with Authentication and Json media type headers. @@ -1662,6 +1710,47 @@ The type of the object to deserialize. The instance of being deserialized. + + + Additional configuration based on the connector requirements + + Any property added to this dictionary will be passed in the same case that is provided + + + + ID of the application + + + + + Name of the application. + + + + + Indicates whether the app is visible. + + + + + ID of the apps underlying connector. + + + + + Type of app + + + + + The date the app was created. + + + + + The date the app was last updated. + + Information about the device being used. diff --git a/src/OneLoginClient/OneLoginClient.Apps.cs b/src/OneLoginClient/OneLoginClient.Apps.cs new file mode 100644 index 0000000..6afbcea --- /dev/null +++ b/src/OneLoginClient/OneLoginClient.Apps.cs @@ -0,0 +1,38 @@ +using OneLogin.Requests; +using OneLogin.Responses; +using OneLogin.Types; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace OneLogin +{ + public partial class OneLoginClient + { + public async Task GetApps(string name = null, int? connectorId = null, AppAuthMethod? authMethod = null) + { + var parameters = new Dictionary + { + {"name", name}, + {"connector_id", connectorId.HasValue ? connectorId.ToString() : string.Empty}, + {"auth_method", authMethod.HasValue ? authMethod.ToString() : string.Empty} + }; + return await GetResource($"/{Endpoints.API}{Endpoints.ApiVersion2}{Endpoints.ONELOGIN_APPS}", parameters); + } + + public async Task GetAppById(int appId) + { + return await GetResource($"/{Endpoints.API}{Endpoints.ApiVersion2}{Endpoints.ONELOGIN_APPS}/{appId}"); + } + + public async Task UpdateApp(UpdateAppRequest app) + { + return await PutResource($"/{Endpoints.API}{Endpoints.ApiVersion2}{Endpoints.ONELOGIN_APPS}/{app.Id}", app); + } + + public async Task CreateApp(CreateAppRequest app) + { + return await PostResource($"/{Endpoints.API}{Endpoints.ApiVersion2}{Endpoints.ONELOGIN_APPS}", app); + } + } +} diff --git a/src/OneLoginClient/OneLoginClient.Events.cs b/src/OneLoginClient/OneLoginClient.Events.cs index 4eac9c4..c3579d8 100644 --- a/src/OneLoginClient/OneLoginClient.Events.cs +++ b/src/OneLoginClient/OneLoginClient.Events.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using OneLogin.Requests; using OneLogin.Responses; @@ -46,14 +45,9 @@ public async Task GetEvents(string clientId = null, DateTime? {"since", since.ToString()}, {"until", until.ToString()}, {"user_id", userId.ToString()}, - } - .Where(kv => !string.IsNullOrWhiteSpace(kv.Value)) - .Select(kv => $"{kv.Key}={kv.Value}") - .ToList(); + }; - var url = $"{Endpoints.ONELOGIN_EVENTS}{(parameters.Any() ? "?" : "")}" + string.Join("&", parameters); - - return await GetResource(url); + return await GetResource(Endpoints.ONELOGIN_EVENTS, parameters); } /// diff --git a/src/OneLoginClient/OneLoginClient.Users.cs b/src/OneLoginClient/OneLoginClient.Users.cs index e3dc2cf..e0f3486 100644 --- a/src/OneLoginClient/OneLoginClient.Users.cs +++ b/src/OneLoginClient/OneLoginClient.Users.cs @@ -31,13 +31,9 @@ public async Task GetUsers(string directoryId = null, string e {"until", until.ToString()}, {"username", userName}, {"userprincipalname", userPrincipalName} - } - .Where(kv => !string.IsNullOrWhiteSpace(kv.Value)) - .Select(kv => $"{kv.Key}={kv.Value}") - .ToList(); + }; - var url = $"{Endpoints.ONELOGIN_USERS}{(parameters.Any() ? "?" : "")}" + string.Join("&", parameters); - return await GetResource(url); + return await GetResource(Endpoints.ONELOGIN_USERS, parameters); } /// diff --git a/src/OneLoginClient/OneLoginClient.cs b/src/OneLoginClient/OneLoginClient.cs index 47ca016..903a5ed 100644 --- a/src/OneLoginClient/OneLoginClient.cs +++ b/src/OneLoginClient/OneLoginClient.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; @@ -132,7 +133,30 @@ public async Task> GetPreviousPages(T source, int? pages = null) wher return results; } + /// + /// Utility method to call OneLogin GET API endpoints and deserialize response + /// + /// The type of the object to deserialize. + /// The path of the API endpoint + /// A dictionary of query string parameters + /// + public async Task GetResource(string path, Dictionary queryParameters) + { + var parameters = queryParameters + .Where(kv => !string.IsNullOrWhiteSpace(kv.Value)) + .Select(kv => $"{kv.Key}={kv.Value}") + .ToList(); + var url = $"{path}{(parameters.Any() ? "?" : "")}" + string.Join("&", parameters); + return await GetResource(url); + } + + /// + /// Utility method to call OneLogin GET API endpoints and deserialize response + /// + /// The type of the object to deserialize. + /// The relative URL (path and optional query string) of the API endpoint + /// public async Task GetResource(string url) { if (string.IsNullOrWhiteSpace(url)) { throw new ArgumentException(nameof(url)); } @@ -142,6 +166,13 @@ public async Task GetResource(string url) return response; } + /// + /// Utility method to call OneLogin POST API endpoints and deserialize response + /// + /// The type of the object to deserialize. + /// The relative URL (path and optional query string) of the API endpoint + /// The object to serialize + /// public async Task PostResource(string url, object obj) { if (obj == null) throw new ArgumentNullException(nameof(obj)); @@ -152,6 +183,13 @@ public async Task PostResource(string url, object obj) return response; } + /// + /// Utility method to call OneLogin PUT API endpoints and deserialize response + /// + /// The type of the object to deserialize. + /// The relative URL (path and optional query string) of the API endpoint + /// The object to serialize + /// public async Task PutResource(string url, object obj) { if (obj == null) throw new ArgumentNullException(nameof(obj)); @@ -162,6 +200,12 @@ public async Task PutResource(string url, object obj) return response; } + /// + /// Utility method to call OneLogin DELETE API endpoints and deserialize response + /// + /// The type of the object to deserialize. + /// The relative URL (path and optional query string) of the API endpoint + /// public async Task DeleteResource(string url) { if (string.IsNullOrWhiteSpace(url)) { throw new ArgumentException(nameof(url)); } diff --git a/src/OneLoginClient/Requests/CreateAppRequest.cs b/src/OneLoginClient/Requests/CreateAppRequest.cs new file mode 100644 index 0000000..f969e12 --- /dev/null +++ b/src/OneLoginClient/Requests/CreateAppRequest.cs @@ -0,0 +1,19 @@ +using OneLogin.Types; +using System; + +namespace OneLogin.Requests +{ + public class CreateAppRequest : AppDetail + { + public CreateAppRequest(int connectorId, string name) + { + if (string.IsNullOrEmpty(name)) + { + throw new ArgumentNullException(nameof(name), "Name is required"); + } + + ConnectorId = connectorId; + Name = name; + } + } +} diff --git a/src/OneLoginClient/Requests/UpdateAppRequest.cs b/src/OneLoginClient/Requests/UpdateAppRequest.cs new file mode 100644 index 0000000..b2111d5 --- /dev/null +++ b/src/OneLoginClient/Requests/UpdateAppRequest.cs @@ -0,0 +1,18 @@ +using OneLogin.Types; +using System; + +namespace OneLogin.Requests +{ + public class UpdateAppRequest : AppDetail + { + public UpdateAppRequest(int appId) + { + if (appId <= 0) + { + throw new ArgumentOutOfRangeException(nameof(appId), "App Id is required in app parameter"); + } + + Id = appId; + } + } +} diff --git a/src/OneLoginClient/Responses/GetAppsResponse.cs b/src/OneLoginClient/Responses/GetAppsResponse.cs new file mode 100644 index 0000000..d5f8a58 --- /dev/null +++ b/src/OneLoginClient/Responses/GetAppsResponse.cs @@ -0,0 +1,11 @@ +using OneLogin.Types; +using System; +using System.Collections.Generic; + +namespace OneLogin.Responses +{ + public class GetAppsResponse : List + { + + } +} diff --git a/src/OneLoginClient/Serializer.cs b/src/OneLoginClient/Serializer.cs index ddd57fd..e0bbc4c 100644 --- a/src/OneLoginClient/Serializer.cs +++ b/src/OneLoginClient/Serializer.cs @@ -8,7 +8,7 @@ namespace OneLogin /// public class Serializer { - private static readonly JsonSerializer _serializer = new JsonSerializer { NullValueHandling = NullValueHandling.Ignore }; + private static readonly JsonSerializer _serializer = new JsonSerializer { NullValueHandling = NullValueHandling.Ignore, ContractResolver = new SnakeCaseContractResolver() }; /// /// Serializes the specified object to a JSON string. diff --git a/src/OneLoginClient/SnakeCaseContractResolver.cs b/src/OneLoginClient/SnakeCaseContractResolver.cs new file mode 100644 index 0000000..2c9e74a --- /dev/null +++ b/src/OneLoginClient/SnakeCaseContractResolver.cs @@ -0,0 +1,14 @@ +using Newtonsoft.Json.Serialization; +using System.Text.RegularExpressions; + +namespace OneLogin +{ + internal class SnakeCaseContractResolver : DefaultContractResolver + { + protected internal Regex converter = new Regex(@"((?<=[a-z])(?[A-Z0-9])|(?<=[^_])(?[A-Z][a-z]))"); + protected override string ResolvePropertyName(string propertyName) + { + return converter.Replace(propertyName, "_${b}").ToLower(); + } + } +} diff --git a/src/OneLoginClient/Types/AppAuthMethod.cs b/src/OneLoginClient/Types/AppAuthMethod.cs new file mode 100644 index 0000000..49dee6d --- /dev/null +++ b/src/OneLoginClient/Types/AppAuthMethod.cs @@ -0,0 +1,16 @@ +using System; + +namespace OneLogin.Types +{ + public enum AppAuthMethod + { + Password = 0, + OpenId = 1, + SAML = 2, + API = 3, + Google = 4, + FormsBasedApp = 6, + WSFED = 7, + OpenIdConnect = 8 + } +} diff --git a/src/OneLoginClient/Types/AppConfiguration.cs b/src/OneLoginClient/Types/AppConfiguration.cs new file mode 100644 index 0000000..a3f9b4c --- /dev/null +++ b/src/OneLoginClient/Types/AppConfiguration.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; + +namespace OneLogin.Types +{ + public class AppConfiguration + { + /// + /// Additional configuration based on the connector requirements + /// + /// Any property added to this dictionary will be passed in the same case that is provided + [JsonExtensionData] + public Dictionary CustomProperties { get; set; } + } + +} diff --git a/src/OneLoginClient/Types/AppDetail.cs b/src/OneLoginClient/Types/AppDetail.cs new file mode 100644 index 0000000..dcb2398 --- /dev/null +++ b/src/OneLoginClient/Types/AppDetail.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; +using OneLogin.Converters; +using System; +using System.Collections.Generic; + +namespace OneLogin.Types +{ + [JsonConverter(typeof(AppConverter))] + public class AppDetail : AppSummary + { + [JsonIgnore] + public AppSso SSO { get; protected set; } + [JsonProperty(nameof(SSO))] + private AppSso SSOSetter { set => SSO = value; } + + public AppConfiguration Configuration { get; set; } + public Dictionary Parameters { get; set; } + } +} diff --git a/src/OneLoginClient/Types/AppParameter.cs b/src/OneLoginClient/Types/AppParameter.cs new file mode 100644 index 0000000..36b2b43 --- /dev/null +++ b/src/OneLoginClient/Types/AppParameter.cs @@ -0,0 +1,14 @@ +using System; + +namespace OneLogin.Types +{ + public class AppParameter + { + public int Id { get; set; } + public string UserAttributeMappings { get; set; } + public string AttributesTransformations { get; set; } + public string Label { get; set; } + public bool? SkipIfBlank { get; set; } + public bool? ProvisionedEntitlements { get; set; } + } +} diff --git a/src/OneLoginClient/Types/AppSamlConfiguration.cs b/src/OneLoginClient/Types/AppSamlConfiguration.cs new file mode 100644 index 0000000..f674046 --- /dev/null +++ b/src/OneLoginClient/Types/AppSamlConfiguration.cs @@ -0,0 +1,10 @@ +using System; + +namespace OneLogin.Types +{ + public class AppSamlConfiguration : AppConfiguration + { + public string SignatureAlgorithm { get; set; } + public int? CertificateId { get; set; } + } +} diff --git a/src/OneLoginClient/Types/AppSso.cs b/src/OneLoginClient/Types/AppSso.cs new file mode 100644 index 0000000..48da07d --- /dev/null +++ b/src/OneLoginClient/Types/AppSso.cs @@ -0,0 +1,11 @@ +using System; + +namespace OneLogin.Types +{ + public class AppSso + { + public string MetadataUrl { get; set; } + + public AppSsoCertificate Certificate { get; set; } + } +} diff --git a/src/OneLoginClient/Types/AppSsoCertificate.cs b/src/OneLoginClient/Types/AppSsoCertificate.cs new file mode 100644 index 0000000..1a893b7 --- /dev/null +++ b/src/OneLoginClient/Types/AppSsoCertificate.cs @@ -0,0 +1,13 @@ +using System; + +namespace OneLogin.Types +{ + public class AppSsoCertificate + { + public string Name { get; set; } + + public string Value { get; set; } + + public int Id { get; set; } + } +} diff --git a/src/OneLoginClient/Types/AppSummary.cs b/src/OneLoginClient/Types/AppSummary.cs new file mode 100644 index 0000000..a287c33 --- /dev/null +++ b/src/OneLoginClient/Types/AppSummary.cs @@ -0,0 +1,42 @@ +using System; + +namespace OneLogin.Types +{ + public class AppSummary + { + /// + /// ID of the application + /// + public int Id { get; set; } + + /// + /// Name of the application. + /// + public string Name { get; set; } + + /// + /// Indicates whether the app is visible. + /// + public bool? Visible { get; set; } + + /// + /// ID of the apps underlying connector. + /// + public int? ConnectorId { get; set; } + + /// + /// Type of app + /// + public AppAuthMethod? AuthMethod { get; set; } + + /// + /// The date the app was created. + /// + public DateTime? CreatedAt { get; set; } + + /// + /// The date the app was last updated. + /// + public DateTime? UpdatedAt { get; set; } + } +}