Skip to content

Commit 7edca45

Browse files
committed
Environment var => IOptions
1 parent 7d3cafc commit 7edca45

File tree

9 files changed

+73
-88
lines changed

9 files changed

+73
-88
lines changed
Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,29 @@
1+
using Ahk.GradeManagement.Backend.Common.Options;
2+
13
using Microsoft.AspNetCore.Mvc;
4+
using Microsoft.Extensions.Options;
25

36
namespace Ahk.GradeManagement.Api.Controllers;
47

58
[ApiController]
69
[Route("api/config")]
7-
public class ConfigController : ControllerBase
10+
public class ConfigController(IOptions<AhkOptions> ahkOptionsAccessor) : ControllerBase
811
{
12+
private readonly AhkOptions _ahkOptions = ahkOptionsAccessor.Value;
13+
914
[HttpGet("app-url")]
1015
[ProducesResponseType(StatusCodes.Status200OK)]
1116
[ProducesResponseType(StatusCodes.Status404NotFound)]
1217
public IActionResult GetAppUrl()
1318
{
14-
var appUrl = Environment.GetEnvironmentVariable("APP_URL");
15-
16-
if (string.IsNullOrEmpty(appUrl))
17-
return NotFound("APP_URL is not set.");
18-
19-
return Ok(appUrl);
19+
return Ok(_ahkOptions.AppUrl);
2020
}
2121

2222
[HttpGet("monitor-url")]
2323
[ProducesResponseType(StatusCodes.Status200OK)]
2424
[ProducesResponseType(StatusCodes.Status404NotFound)]
2525
public IActionResult GetMonitorUrl()
2626
{
27-
var monitorUrl = Environment.GetEnvironmentVariable("MONITOR_URL");
28-
29-
if (string.IsNullOrEmpty(monitorUrl))
30-
return NotFound("MONITOR_URL is not set.");
31-
32-
return Ok(monitorUrl);
27+
return Ok(_ahkOptions.MonitorUrl);
3328
}
3429
}
Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
using Ahk.GradeManagement.Backend.Common.Options;
12
using Ahk.GradeManagement.Shared.Dtos.GitHubManifest;
23

34
using Azure.Identity;
45
using Azure.Security.KeyVault.Secrets;
56

67
using Microsoft.AspNetCore.Mvc;
8+
using Microsoft.Extensions.Options;
79

810
using System.Net.Http.Headers;
911
using System.Text.Json;
@@ -12,14 +14,14 @@ namespace Ahk.GradeManagement.Api.Controllers;
1214

1315
[Route("api/github")]
1416
[ApiController]
15-
public class GitHubAppController(IHttpClientFactory httpClientFactory) : ControllerBase
17+
public class GitHubAppController(IHttpClientFactory httpClientFactory, IOptions<AhkOptions> ahkOptionsAccessor) : ControllerBase
1618
{
19+
private readonly AhkOptions _ahkOptions = ahkOptionsAccessor.Value;
20+
1721
[HttpGet]
1822
public async Task<IActionResult> CreateGitHubApp([FromQuery] string code)
1923
{
20-
var appUrl = Environment.GetEnvironmentVariable("APP_URL");
21-
var keyVaultUrl = Environment.GetEnvironmentVariable("KEY_VAULT_URI");
22-
var client = new SecretClient(new Uri(keyVaultUrl), new DefaultAzureCredential());
24+
var client = new SecretClient(new Uri(_ahkOptions.KeyVaultUrl), new DefaultAzureCredential());
2325
var httpClient = httpClientFactory.CreateClient();
2426
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
2527

@@ -30,21 +32,14 @@ public async Task<IActionResult> CreateGitHubApp([FromQuery] string code)
3032

3133
var content = await response.Content.ReadAsStringAsync();
3234
var gitHubApp = JsonSerializer.Deserialize<GitHubApp>(content);
33-
var pem = RemovePemFencing(gitHubApp.Pem);
3435

35-
await client.SetSecretAsync($"GitHubMonitorConfig--{gitHubApp.Owner.Login}--GitHubAppId",
36-
gitHubApp.Id.ToString());
36+
var lines = gitHubApp.Pem.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
37+
var pem = string.Join("", lines.Skip(1).Take(lines.Length - 2));
38+
39+
await client.SetSecretAsync($"GitHubMonitorConfig--{gitHubApp.Owner.Login}--GitHubAppId", gitHubApp.Id.ToString());
3740
await client.SetSecretAsync($"GitHubMonitorConfig--{gitHubApp.Owner.Login}--GitHubAppPrivateKey", pem);
38-
await client.SetSecretAsync($"GitHubMonitorConfig--{gitHubApp.Owner.Login}--GitHubWebhookSecret",
39-
gitHubApp.WebhookSecret);
41+
await client.SetSecretAsync($"GitHubMonitorConfig--{gitHubApp.Owner.Login}--GitHubWebhookSecret", gitHubApp.WebhookSecret);
4042

4143
return Redirect($"https://github.com/organizations/ahk-dev-org/settings/apps/{gitHubApp.Slug}/installations");
4244
}
43-
44-
public static string RemovePemFencing(string pem)
45-
{
46-
var lines = pem.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
47-
var base64String = string.Join("", lines.Skip(1).Take(lines.Length - 2));
48-
return base64String;
49-
}
5045
}

src/Ahk.GradeManagement/Ahk.GradeManagement.Api/Program.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Ahk.GradeManagement.Api.Authorization;
22
using Ahk.GradeManagement.Api.ErrorHandling;
33
using Ahk.GradeManagement.Api.RequestContext;
4+
using Ahk.GradeManagement.Backend.Common.Options;
45
using Ahk.GradeManagement.Bll;
56
using Ahk.GradeManagement.Dal;
67

@@ -20,13 +21,8 @@ public static void Main(string[] args)
2021
{
2122
var builder = WebApplication.CreateBuilder(args);
2223

23-
var keyVaultUri = builder.Configuration["KEY_VAULT_URI"];
24-
if (!string.IsNullOrEmpty(keyVaultUri))
25-
{
26-
builder.Configuration.AddAzureKeyVault(
27-
new Uri(keyVaultUri),
28-
new DefaultAzureCredential());
29-
}
24+
var ahkOptions = builder.Services.ConfigureOption<AhkOptions>(builder.Configuration);
25+
builder.Configuration.AddAzureKeyVault(new Uri(ahkOptions.KeyVaultUrl), new DefaultAzureCredential());
3026

3127
builder.Services.AddRequestContext();
3228
builder.Services.AddHttpClient();

src/Ahk.GradeManagement/Ahk.GradeManagement.Api/Properties/launchSettings.json

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,14 @@
99
}
1010
},
1111
"profiles": {
12-
"http": {
13-
"commandName": "Project",
14-
"dotnetRunMessages": true,
15-
"launchBrowser": true,
16-
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
17-
"applicationUrl": "http://10.0.0.10:5180",
18-
"environmentVariables": {
19-
"ASPNETCORE_ENVIRONMENT": "Development",
20-
"MONITOR_URL": "https://monitor.mm-home.eu/api/github-webhook",
21-
"KEY_VAULT_URI": "https://ahkgithubmonitorsecrets.vault.azure.net/",
22-
"APP_URL": "https://app.mm-home.eu/"
23-
}
24-
},
2512
"https": {
2613
"commandName": "Project",
2714
"dotnetRunMessages": true,
2815
"launchBrowser": true,
2916
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
3017
"applicationUrl": "https://localhost:7111;http://localhost:5268",
3118
"environmentVariables": {
32-
"ASPNETCORE_ENVIRONMENT": "Development",
33-
"MONITOR_URL": "https://monitor.mm-home.eu/api/github-webhook",
34-
"KEY_VAULT_URI": "https://ahkgithubmonitorsecrets.vault.azure.net/",
35-
"APP_URL": "https://localhost:7111/"
19+
"ASPNETCORE_ENVIRONMENT": "Development"
3620
}
3721
},
3822
"IIS Express": {

src/Ahk.GradeManagement/Ahk.GradeManagement.Api/appsettings.Development.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,9 @@
1010
},
1111
"ErrorHandlingOptions": {
1212
"ReturnExceptionDetails": true
13+
},
14+
"AhkOptions": {
15+
"AppUrl": "https://localhost:7111/",
16+
"KeyVaultUrl": "https://ahkgithubmonitorsecrets.vault.azure.net/"
1317
}
1418
}

src/Ahk.GradeManagement/Ahk.GradeManagement.Api/appsettings.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"ConnectionStrings": {
3-
"DbConnection": "Server=ahk-grade-management.database.windows.net,1433;Database=GradeManagement.Database;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;MultipleActiveResultSets=true;Authentication=\"Active Directory Default\";"
3+
"DbConnection": "OVERRIDE ME"
44
},
55
"Logging": {
66
"LogLevel": {
@@ -17,5 +17,10 @@
1717
},
1818
"ErrorHandlingOptions": {
1919
"ReturnExceptionDetails": false
20+
},
21+
"AhkOptions": {
22+
"AppUrl": "OVERRIDE ME",
23+
"MonitorUrl": "OVERRIDE ME",
24+
"KeyVaultUrl": "OVERRIDE ME"
2025
}
2126
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace Ahk.GradeManagement.Backend.Common.Options;
2+
3+
public class AhkOptions
4+
{
5+
public string AppUrl { get; set; } = null!;
6+
public string MonitorUrl { get; set; } = null!;
7+
public string KeyVaultUrl { get; set; } = null!;
8+
}

src/Ahk.GradeManagement/Ahk.GradeManagement.Bll/Services/CourseService.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@
1919

2020
using Microsoft.EntityFrameworkCore;
2121
using Ahk.GradeManagement.Backend.Common.RequestContext;
22+
using Microsoft.Extensions.Options;
23+
using Ahk.GradeManagement.Backend.Common.Options;
2224

2325
namespace Ahk.GradeManagement.Bll.Services;
2426

25-
public class CourseService(GradeManagementDbContext gradeManagementDbContext, IMapper mapper, IRequestContext requestContext)
27+
public class CourseService(GradeManagementDbContext gradeManagementDbContext, IMapper mapper, IRequestContext requestContext, IOptions<AhkOptions> ahkOptionsAccessor)
2628
: ICrudServiceBase<CourseRequest, CourseResponse>
2729
{
30+
private readonly AhkOptions _ahkOptions = ahkOptionsAccessor.Value;
31+
2832
public async Task<IEnumerable<CourseResponse>> GetAllAsync()
2933
{
3034
return await gradeManagementDbContext.Course
@@ -114,11 +118,9 @@ public async Task<IEnumerable<GroupResponse>> GetAllGroupsByIdAsync(long id)
114118
.ToListAsync();
115119
}
116120

117-
private static async Task SetSecret(string moodleClientId, string privateKey)
121+
private async Task SetSecret(string moodleClientId, string privateKey)
118122
{
119-
var keyVaultUrl = Environment.GetEnvironmentVariable("KEY_VAULT_URI");
120-
if (string.IsNullOrEmpty(keyVaultUrl)) throw new ArgumentException("Key vault URL is null or empty!");
121-
var client = new SecretClient(new Uri(keyVaultUrl), new DefaultAzureCredential());
123+
var client = new SecretClient(new Uri(_ahkOptions.KeyVaultUrl), new DefaultAzureCredential());
122124

123125
await client.SetSecretAsync($"{MoodleConfig.Name}--{moodleClientId}--MoodlePrivateKey", privateKey);
124126
}

src/Ahk.GradeManagement/Ahk.GradeManagement.Bll/Services/Moodle/TokenGeneratorService.cs

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,34 @@
1+
using Ahk.GradeManagement.Backend.Common.Options;
12
using Ahk.GradeManagement.Dal.Entities;
23
using Ahk.GradeManagement.Shared.Config;
4+
using Ahk.GradeManagement.Shared.Exceptions;
35

46
using Azure.Identity;
57
using Azure.Security.KeyVault.Secrets;
68

7-
using Ahk.GradeManagement.Shared.Exceptions;
8-
99
using Jose;
1010

11-
using Microsoft.Extensions.Configuration;
11+
using Microsoft.Extensions.Options;
1212

1313
using Org.BouncyCastle.Crypto;
1414
using Org.BouncyCastle.Crypto.Parameters;
1515
using Org.BouncyCastle.OpenSsl;
1616
using Org.BouncyCastle.Security;
1717

18+
using System.Globalization;
1819
using System.Net.Http.Json;
1920
using System.Security.Claims;
2021
using System.Security.Cryptography;
2122

2223
namespace Ahk.GradeManagement.Bll.Services.Moodle;
2324

24-
public class TokenGeneratorService(IConfiguration configuration)
25+
public class TokenGeneratorService(IOptions<AhkOptions> ahkOptionsAccessor)
2526
{
26-
private const string Scope =
27-
"https://purl.imsglobal.org/spec/lti-ags/scope/lineitem https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly https://purl.imsglobal.org/spec/lti-ags/scope/score";
27+
private readonly AhkOptions _ahkOptions = ahkOptionsAccessor.Value;
28+
private static readonly string Scope = "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem " +
29+
"https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly " +
30+
"https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly " +
31+
"https://purl.imsglobal.org/spec/lti-ags/scope/score";
2832

2933
public async Task<string?> GenerateAccessToken(Course course)
3034
{
@@ -35,9 +39,8 @@ public class TokenGeneratorService(IConfiguration configuration)
3539

3640
var data = new[]
3741
{
38-
new KeyValuePair<string, string>("grant_type", "client_credentials"), new KeyValuePair<string, string>(
39-
"client_assertion_type",
40-
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer"),
42+
new KeyValuePair<string, string>("grant_type", "client_credentials"),
43+
new KeyValuePair<string, string>("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"),
4144
new KeyValuePair<string, string>("client_assertion", token),
4245
new KeyValuePair<string, string>("scope", Scope),
4346
};
@@ -49,41 +52,34 @@ public class TokenGeneratorService(IConfiguration configuration)
4952

5053
private async Task<string> MakeClientAssertionToken(Course course)
5154
{
52-
var appUrl = configuration["APP_URL"];
53-
if (string.IsNullOrEmpty(appUrl)) throw new MoodleSyncException("App url was null or empty!");
54-
55-
var claims = new List<Claim>();
56-
claims.Add(new Claim("iss", appUrl));
57-
claims.Add(new Claim("iat", ((int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds).ToString()));
58-
claims.Add(new Claim("exp",
59-
((int)(DateTime.UtcNow.AddHours(1) - new DateTime(1970, 1, 1)).TotalSeconds).ToString()));
60-
claims.Add(new Claim("aud", "https://edu.vik.bme.hu/mod/lti/token.php"));
61-
claims.Add(new Claim("sub", course.MoodleClientId));
55+
List<Claim> claims =
56+
[
57+
new Claim("iss", _ahkOptions.AppUrl),
58+
new Claim("iat", ((int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds).ToString(CultureInfo.InvariantCulture)),
59+
new Claim("exp", ((int)(DateTime.UtcNow.AddHours(1) - new DateTime(1970, 1, 1)).TotalSeconds).ToString(CultureInfo.InvariantCulture)),
60+
new Claim("aud", "https://edu.vik.bme.hu/mod/lti/token.php"),
61+
new Claim("sub", course.MoodleClientId)
62+
];
6263

6364
return await CreateToken(claims, course);
6465
}
6566

6667
private async Task<string> CreateToken(List<Claim> claims, Course course)
6768
{
68-
var keyVaultUrl = Environment.GetEnvironmentVariable("KEY_VAULT_URI");
69-
if (keyVaultUrl == null)
70-
throw new Exception("Please set environment variable KEY_VAULT_URI");
71-
72-
var secretClient = new SecretClient(new Uri(keyVaultUrl), new DefaultAzureCredential());
73-
var moodlePrivateKey =
74-
await secretClient.GetSecretAsync($"{MoodleConfig.Name}--{course.MoodleClientId}--MoodlePrivateKey");
69+
var secretClient = new SecretClient(new Uri(_ahkOptions.KeyVaultUrl), new DefaultAzureCredential());
70+
var moodlePrivateKey = await secretClient.GetSecretAsync($"{MoodleConfig.Name}--{course.MoodleClientId}--MoodlePrivateKey");
7571

7672
var privateRsaKey = moodlePrivateKey.Value.Value;
77-
if (string.IsNullOrEmpty(privateRsaKey)) throw new MoodleSyncException("Private RSA was null or empty!");
73+
if (string.IsNullOrEmpty(privateRsaKey))
74+
throw new MoodleSyncException("Private RSA was null or empty!");
7875

7976
privateRsaKey = privateRsaKey.Trim();
8077

8178
RSAParameters rsaParams;
8279
using (var tr = new StringReader(privateRsaKey))
8380
{
8481
var pemReader = new PemReader(tr);
85-
var keyPair = pemReader.ReadObject() as AsymmetricCipherKeyPair;
86-
if (keyPair == null)
82+
if (pemReader.ReadObject() is not AsymmetricCipherKeyPair keyPair)
8783
throw new MoodleSyncException("Could not read RSA private key");
8884

8985
var privateRsaParams = keyPair.Private as RsaPrivateCrtKeyParameters;

0 commit comments

Comments
 (0)