From b369cc8e11002fd12dbbad6f6b24c6deb93e908c Mon Sep 17 00:00:00 2001 From: Priyasunil26 <158257824+Priyasunil26@users.noreply.github.com> Date: Tue, 7 Oct 2025 21:32:10 +0530 Subject: [PATCH 1/9] Update Dockerfile --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index 3d4ddf665..70cb1b376 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,6 +38,7 @@ RUN dotnet publish -c Release -o out FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS job WORKDIR /app COPY --from=job-publish /app/src/Exceptionless.Job/out ./ +COPY src/Exceptionless.Core/Mail/Templates /app/src/Exceptionless.Core/Mail/Templates EXPOSE 8080 @@ -55,6 +56,7 @@ RUN dotnet publish -c Release -o out /p:SkipSpaPublish=true FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS api WORKDIR /app COPY --from=api-publish /app/src/Exceptionless.Web/out ./ +COPY src/Exceptionless.Core/Mail/Templates /app/src/Exceptionless.Core/Mail/Templates EXPOSE 8080 From f75f5953300bdba6a8332ada5d151ea29acbb58b Mon Sep 17 00:00:00 2001 From: Priyasunil26 <158257824+Priyasunil26@users.noreply.github.com> Date: Tue, 7 Oct 2025 22:03:30 +0530 Subject: [PATCH 2/9] Update docker-compose.yml --- docker/docker-compose.yml | 55 +++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 8b8f295ee..b49bee93c 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -8,11 +8,60 @@ services: xpack.security.enabled: "false" action.destructive_requires_name: false ES_JAVA_OPTS: -Xms1g -Xmx1g + ulimits: + memlock: + soft: -1 + hard: -1 + networks: + - app_net ports: - 9200:9200 volumes: - - esdata:/usr/share/elasticsearch/data + - /home/syncfusion/esdata:/usr/share/elasticsearch/data + restart: unless-stopped + exceptionless: + build: + context: ../ + dockerfile: Dockerfile + target: app + image: exceptionless + container_name: exp + depends_on: + - elasticsearch + environment: + EX_ConnectionStrings__Elasticsearch: http://elasticsearch:9200 + ports: + - "5200:8080" + networks: + - app_net + volumes: + - /home/syncfusion/appdata:/app/storage + restart: unless-stopped + + nginx: + image: nginx:latest + ports: + - "80:80" + - "443:443" + volumes: + - /home/syncfusion/nginx/nginx.conf:/etc/nginx/conf.d/default.conf + - /home/syncfusion/ssl/certs:/etc/ssl/certs + - /home/syncfusion/ssl/private:/etc/ssl/private + networks: + - app_net + depends_on: + - exceptionless + + networks: + app_net: + driver: bridge + + volumes: + esdata: + driver: local + appdata: + driver: local kibana: depends_on: - elasticsearch @@ -32,7 +81,3 @@ services: ports: - 8025:8025 - 1025:1025 - -volumes: - esdata: - driver: local From 7f15cdb47a7cd50673ab156974ee52cf50d80847 Mon Sep 17 00:00:00 2001 From: Priyasunil26 <158257824+Priyasunil26@users.noreply.github.com> Date: Tue, 7 Oct 2025 22:14:38 +0530 Subject: [PATCH 3/9] Update AuthOptions.cs --- src/Exceptionless.Core/Configuration/AuthOptions.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Exceptionless.Core/Configuration/AuthOptions.cs b/src/Exceptionless.Core/Configuration/AuthOptions.cs index f394c32f9..cd7444970 100644 --- a/src/Exceptionless.Core/Configuration/AuthOptions.cs +++ b/src/Exceptionless.Core/Configuration/AuthOptions.cs @@ -27,6 +27,10 @@ public class AuthOptions public string? LdapConnectionString { get; internal set; } + public string? AADAppId { get; private set; } + + public string? AADAppSecret { get; private set; } + public static AuthOptions ReadFromConfiguration(IConfiguration config) { var options = new AuthOptions(); @@ -45,6 +49,8 @@ public static AuthOptions ReadFromConfiguration(IConfiguration config) options.FacebookSecret = oAuth.GetString(nameof(options.FacebookSecret)); options.GitHubId = oAuth.GetString(nameof(options.GitHubId)); options.GitHubSecret = oAuth.GetString(nameof(options.GitHubSecret)); + options.AADAppId = oAuth.GetString(nameof(options.AADAppId)); + options.AADAppSecret = oAuth.GetString(nameof(options.AADAppSecret)); return options; } From c5db84b2d230dafd63b13b61881cce86bbc4d51a Mon Sep 17 00:00:00 2001 From: Priyasunil26 <158257824+Priyasunil26@users.noreply.github.com> Date: Tue, 7 Oct 2025 22:24:30 +0530 Subject: [PATCH 4/9] Update login-controller.js --- .../ClientApp.angular/app/auth/login-controller.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Exceptionless.Web/ClientApp.angular/app/auth/login-controller.js b/src/Exceptionless.Web/ClientApp.angular/app/auth/login-controller.js index 7ac079b77..3bcc27086 100644 --- a/src/Exceptionless.Web/ClientApp.angular/app/auth/login-controller.js +++ b/src/Exceptionless.Web/ClientApp.angular/app/auth/login-controller.js @@ -71,6 +71,8 @@ return !!GOOGLE_APPID; case "live": return !!LIVE_APPID; + case 'oauth2': + return true; default: return false; } From 4ff39a9b5abbf2961bea059499f39844dad9d917 Mon Sep 17 00:00:00 2001 From: Priyasunil26 <158257824+Priyasunil26@users.noreply.github.com> Date: Tue, 7 Oct 2025 23:11:37 +0530 Subject: [PATCH 5/9] Update login.tpl.html --- .../ClientApp.angular/app/auth/login.tpl.html | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Exceptionless.Web/ClientApp.angular/app/auth/login.tpl.html b/src/Exceptionless.Web/ClientApp.angular/app/auth/login.tpl.html index f26f7dc9d..5de727c76 100644 --- a/src/Exceptionless.Web/ClientApp.angular/app/auth/login.tpl.html +++ b/src/Exceptionless.Web/ClientApp.angular/app/auth/login.tpl.html @@ -19,10 +19,17 @@ autocomplete="on" >
-

- {{::'Log in' | translate}} - {{::'with' | translate}} -

+

Login with Syncfusion account

+
+ +
Date: Tue, 7 Oct 2025 23:44:29 +0530 Subject: [PATCH 6/9] Update satellizer.js --- .../components/satellizer/satellizer.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Exceptionless.Web/ClientApp.angular/components/satellizer/satellizer.js b/src/Exceptionless.Web/ClientApp.angular/components/satellizer/satellizer.js index 17424cfda..1648f86d5 100644 --- a/src/Exceptionless.Web/ClientApp.angular/components/satellizer/satellizer.js +++ b/src/Exceptionless.Web/ClientApp.angular/components/satellizer/satellizer.js @@ -40,6 +40,19 @@ oauthType: "2.0", popupOptions: { width: 580, height: 400 }, }, + oauth2: { + name: 'aad', // whatever you want to call it + clientId: + ‘clientID’, // this must be an app registered inside the Office 365 account, not a personal account – add Id here + url: '/auth/aad', + authorizationEndpoint: + 'https://login.microsoftonline.com/tenantID/oauth2/authorize', + redirectUri: window.location.origin + '/', //for Cordova apps + requiredUrlParams: ['scope'], + //scope: ['user.read'], + scopeDelimiter: ' ', + oauthType: '2.0' + }, google: { name: "google", url: "/auth/google", From d5cd90b3bb707218a2aa9cc4a3fb83f02a58ceb1 Mon Sep 17 00:00:00 2001 From: Priyasunil26 <158257824+Priyasunil26@users.noreply.github.com> Date: Wed, 8 Oct 2025 00:04:44 +0530 Subject: [PATCH 7/9] Update AuthController.cs --- .../Controllers/AuthController.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Exceptionless.Web/Controllers/AuthController.cs b/src/Exceptionless.Web/Controllers/AuthController.cs index 0e940681e..0a64b6e09 100644 --- a/src/Exceptionless.Web/Controllers/AuthController.cs +++ b/src/Exceptionless.Web/Controllers/AuthController.cs @@ -70,6 +70,21 @@ public AuthController(AuthOptions authOptions, IOrganizationRepository organizat /// Validation error [AllowAnonymous] [Consumes("application/json")] + [HttpPost("aad")] + public Task> AADAsync(ExternalAuthInfo value) + { + return ExternalLoginAsync(value, + _authOptions.AADAppId, + _authOptions.AADAppSecret, + (f, c) => + { + c.Scope = "openid email profile"; + return new AADClient(f, c); + } + ); + } + [AllowAnonymous] + [Consumes("application/json")] [HttpPost("login")] public async Task> LoginAsync(Login model) { From 8433f354c8ca08d82fad64409516a7639fd337c0 Mon Sep 17 00:00:00 2001 From: Priyasunil26 <158257824+Priyasunil26@users.noreply.github.com> Date: Wed, 8 Oct 2025 00:15:28 +0530 Subject: [PATCH 8/9] Create AADClient.cs --- src/Exceptionless.Web/Utility/AADClient.cs | 135 +++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 src/Exceptionless.Web/Utility/AADClient.cs diff --git a/src/Exceptionless.Web/Utility/AADClient.cs b/src/Exceptionless.Web/Utility/AADClient.cs new file mode 100644 index 000000000..d2db9741f --- /dev/null +++ b/src/Exceptionless.Web/Utility/AADClient.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using OAuth2.Client; +using OAuth2.Configuration; +using OAuth2.Infrastructure; +using OAuth2.Models; +using RestSharp; +using RestSharp.Authenticators; +using Endpoint = OAuth2.Client.Endpoint; + +namespace Exceptionless.Web.Utility +{ + public class AADClient : OAuth2Client + { + private const string BaseURI = "https://login.microsoftonline.com"; + private static string? TenentId; + + private readonly IRequestFactory _factory; + /// + /// Initializes a new instance of the class. + /// + /// The factory. + /// The configuration. + public AADClient(IRequestFactory factory, IClientConfiguration configuration) + : base(factory, configuration) + { + _factory = factory; + TenentId = "tenantID"; + } + + protected override void BeforeGetAccessToken(BeforeAfterRequestArgs args) + { + args.Request.AddObject(new + { + code = args.Parameters.GetOrThrowUnexpectedResponse("code"), + client_id = Configuration.ClientId, + client_secret = Configuration.ClientSecret, + redirect_uri = Configuration.RedirectUri, + grant_type = "authorization_code", + resource = "https://graph.microsoft.com/" + }); + } + + /// + /// Should return parsed from content received from third-party service. + /// + /// The content which is received from third-party service. + protected override UserInfo ParseUserInfo(string content) + { + var response = JObject.Parse(content); + + return new UserInfo + { + Id = response["id"]?.Value() ?? string.Empty, // Ensure null safety with null-coalescing operator + Email = response["userPrincipalName"]?.SafeGet(x => x.Value()) ?? string.Empty, // Ensure null safety + FirstName = response["givenName"]?.Value() ?? string.Empty, // Ensure null safety + LastName = response["surname"]?.Value() ?? string.Empty // Ensure null safety + }; + } + + /// + /// Friendly name of provider (OAuth2 service). + /// + public override string Name + { + get { return "AAD"; } + } + + /// + /// Defines URI of service which issues access code. + /// + protected override Endpoint AccessCodeServiceEndpoint + { + get { return new Endpoint { BaseUri = BaseURI, Resource = "/" + TenentId + "/oauth2/authorize" }; } + } + + /// + /// Defines URI of service which issues access token. + /// + protected override Endpoint AccessTokenServiceEndpoint + { + get { return new Endpoint { BaseUri = BaseURI, Resource = "/" + TenentId + "/oauth2/token" }; } + } + + /// + /// Defines URI of service which allows to obtain information about user which is currently logged in. + /// + protected override Endpoint UserInfoServiceEndpoint + { + get { return new Endpoint { BaseUri = "https://graph.microsoft.com", Resource = "/v1.0/me" }; } + } + + /// + /// Encoding the user input + /// + public static string HtmlEncode(string name) + { + StringBuilder sbName = new StringBuilder(); + sbName.Append(HttpUtility.HtmlEncode(name)); + name = sbName.ToString(); + return name; + } + protected override async Task GetUserInfoAsync(CancellationToken cancellationToken = default) + { + var client = _factory.CreateClient(UserInfoServiceEndpoint); + var request = _factory.CreateRequest(UserInfoServiceEndpoint); + request.AddHeader("Authorization", string.Format("bearer {0}", AccessToken)); + + BeforeGetUserInfo(new BeforeAfterRequestArgs + { + Client = client, + Request = request, + Configuration = Configuration + }); + + try + { + var response = await client.ExecuteAsync(request, Method.GET, cancellationToken); + var result = ParseUserInfo(response.Content); + result.ProviderName = Name; + + return result; + } + catch (Exception) + { + return new UserInfo(); + } + } + } +} From 97e2936302db7f7ab100a460e84ae2d7df267b1c Mon Sep 17 00:00:00 2001 From: Priyasunil26 <158257824+Priyasunil26@users.noreply.github.com> Date: Wed, 8 Oct 2025 14:04:54 +0530 Subject: [PATCH 9/9] Update Dockerfile --- Dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 70cb1b376..8e6766c33 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,9 @@ COPY ./src/*.props ./src/ COPY ./tests/*.props ./tests/ COPY ./build/packages/* ./build/packages/ +# Copy the Mail Templates +COPY src/Exceptionless.Core/Mail/Templates /app/src/Exceptionless.Core/Mail/Templates + # Copy the main source project files COPY src/*/*.csproj ./ RUN for file in $(ls *.csproj); do mkdir -p src/${file%.*}/ && mv $file src/${file%.*}/; done @@ -38,7 +41,6 @@ RUN dotnet publish -c Release -o out FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS job WORKDIR /app COPY --from=job-publish /app/src/Exceptionless.Job/out ./ -COPY src/Exceptionless.Core/Mail/Templates /app/src/Exceptionless.Core/Mail/Templates EXPOSE 8080 @@ -56,7 +58,6 @@ RUN dotnet publish -c Release -o out /p:SkipSpaPublish=true FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS api WORKDIR /app COPY --from=api-publish /app/src/Exceptionless.Web/out ./ -COPY src/Exceptionless.Core/Mail/Templates /app/src/Exceptionless.Core/Mail/Templates EXPOSE 8080