diff --git a/Dockerfile b/Dockerfile index 7ba7812..52de5f5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ RUN apt update && apt install libldap-2.5-0 -y # Restore as distinct layers RUN dotnet restore ./song_of_the_day/song_of_the_day.csproj # Build and publish a release -RUN dotnet publish ./song_of_the_day/song_of_the_day.csproj -o out +RUN dotnet publish ./song_of_the_day/song_of_the_day.csproj -o out /p:EnvironmentName=Production # Build runtime image FROM mcr.microsoft.com/dotnet/aspnet:9.0 diff --git a/Makefile b/Makefile index 529515c..1583e16 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ .PHONY: issetup issetup: - @[ -f .git/hooks/commit-msg ] || [ $SKIP_MAKE_SETUP_CHECK = "true" ] || (echo "You must run 'make setup' first to initialize the repo!" && exit 1) + @[ -f .git/hooks/commit-msg ] || [ ${SKIP_MAKE_SETUP_CHECK} = "true" ] || (echo "You must run 'make setup' first to initialize the repo!" && exit 1) .PHONY: setup setup: diff --git a/song_of_the_day/.editorconfig b/song_of_the_day/.editorconfig index 0bdc1fd..1dfdd72 100644 --- a/song_of_the_day/.editorconfig +++ b/song_of_the_day/.editorconfig @@ -2,3 +2,5 @@ # CS8981: The type name only contains lower-cased ascii characters. Such names may become reserved for the language. dotnet_diagnostic.CS8981.severity = none +dotnet_diagnostic.CS8602.severity = none +dotnet_diagnostic.CS8604.severity = none diff --git a/song_of_the_day/.vscode/launch.json b/song_of_the_day/.vscode/launch.json new file mode 100644 index 0000000..9fc3e02 --- /dev/null +++ b/song_of_the_day/.vscode/launch.json @@ -0,0 +1,46 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "C#: SongOfTheDay Debug", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "publish Debug", + "program": "${workspaceFolder}/bin/Debug/net9.0/publish/song_of_the_day.dll", + "cwd": "${workspaceFolder}/bin/Debug/net9.0/publish/", + "args": [ + "/p:EnvironmentName=Development" + ], + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development", + "Logging__LogLevel__Microsoft": "Information" + } + }, + { + "name": "C#: SongOfTheDay Production", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "publish Release", + "program": "${workspaceFolder}/bin/Release/net9.0/publish/song_of_the_day.dll", + "cwd": "${workspaceFolder}/bin/Release/net9.0/publish/", + "args": [ + "/p:EnvironmentName=Production" + ], + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Production", + "Logging__LogLevel__Microsoft": "Information" + } + } + ] +} \ No newline at end of file diff --git a/song_of_the_day/.vscode/tasks.json b/song_of_the_day/.vscode/tasks.json new file mode 100644 index 0000000..586f308 --- /dev/null +++ b/song_of_the_day/.vscode/tasks.json @@ -0,0 +1,47 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "publish Debug", + "command": "dotnet", + "type": "shell", + "args": [ + "publish", + // Ask dotnet build to generate full paths for file names. + "/property:GenerateFullPaths=true", + // Do not generate summary otherwise it leads to duplicate errors in Problems panel + "/consoleloggerparameters:NoSummary", + "/p:EnvironmentName=Development", + "-c", + "Debug" + ], + "group": "build", + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$msCompile" + }, + { + "label": "publish Release", + "command": "dotnet", + "type": "shell", + "args": [ + "publish", + // Ask dotnet build to generate full paths for file names. + "/property:GenerateFullPaths=true", + // Do not generate summary otherwise it leads to duplicate errors in Problems panel + "/consoleloggerparameters:NoSummary", + "/p:EnvironmentName=Production", + "-c", + "Release" + ], + "group": "build", + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/song_of_the_day/Auth/LdapAuthenticationService.cs b/song_of_the_day/Auth/LdapAuthenticationService.cs index bd95166..74f0ad8 100644 --- a/song_of_the_day/Auth/LdapAuthenticationService.cs +++ b/song_of_the_day/Auth/LdapAuthenticationService.cs @@ -1,15 +1,16 @@ public class LdapAuthenticationService : IAuthenticationService { private readonly IConfiguration _configuration; + private LdapIntegration _ldapIntegration; - public LdapAuthenticationService(IConfiguration configuration) + public LdapAuthenticationService(IConfiguration configuration, LdapIntegration ldapIntegration) { _configuration = configuration; + _ldapIntegration = ldapIntegration; } public bool Authenticate(string username, string password) { - var ldapInstance = LdapIntegration.Instance; - return ldapInstance == null ? false : ldapInstance.TestLogin(username, password); + return _ldapIntegration == null ? false : _ldapIntegration.TestLogin(username, password); } } \ No newline at end of file diff --git a/song_of_the_day/Auth/PhoneClaimCodeProviderService.cs b/song_of_the_day/Auth/PhoneClaimCodeProviderService.cs index 7c3a0e7..741644d 100644 --- a/song_of_the_day/Auth/PhoneClaimCodeProviderService.cs +++ b/song_of_the_day/Auth/PhoneClaimCodeProviderService.cs @@ -2,11 +2,13 @@ public class PhoneClaimCodeProviderService { private Dictionary _phoneClaimCodes; private Dictionary _phoneClaimNumbers; + private SignalIntegration _signalIntegration; - public PhoneClaimCodeProviderService() + public PhoneClaimCodeProviderService(SignalIntegration signalIntegration) { _phoneClaimCodes = new Dictionary(); _phoneClaimNumbers = new Dictionary(); + _signalIntegration = signalIntegration; } private static Random random = new Random(); @@ -32,7 +34,10 @@ public class PhoneClaimCodeProviderService _phoneClaimNumbers[username] = phoneNumber; } - await SignalIntegration.Instance.SendMessageToUserAsync("Your phone number validation code is: " + generatedCode, phoneNumber); + if (_signalIntegration != null) + { + await _signalIntegration.SendMessageToUserAsync("Your phone number validation code is: " + generatedCode, phoneNumber); + } } public string ValidateClaimCodeForUser(string code, string username) diff --git a/song_of_the_day/LDAPIntegration/LdapIntegration.cs b/song_of_the_day/LDAPIntegration/LdapIntegration.cs index a752701..eb8b066 100644 --- a/song_of_the_day/LDAPIntegration/LdapIntegration.cs +++ b/song_of_the_day/LDAPIntegration/LdapIntegration.cs @@ -6,8 +6,6 @@ using System.Linq; public class LdapIntegration { - public static LdapIntegration? Instance; - private readonly string[] attributesToQuery = new string[] { "uid", @@ -16,12 +14,13 @@ public class LdapIntegration "mail" }; - public LdapIntegration(string uri, int port, string adminBind, string adminPass) + public LdapIntegration(ILogger logger) { - this.Uri = uri; - this.Port = port; - this.AdminBind = adminBind; - this.AdminPass = adminPass; + this.Uri = AppConfiguration.Instance.LDAPConfig.LDAPserver; + this.Port = AppConfiguration.Instance.LDAPConfig.Port; + this.AdminBind = AppConfiguration.Instance.LDAPConfig.Username; + this.AdminPass = AppConfiguration.Instance.LDAPConfig.Password; + this.logger = logger; } private string Uri { get; set; } @@ -32,6 +31,8 @@ public class LdapIntegration private string AdminPass { get; set; } + private ILogger logger { get; set; } + public bool TestLogin(string username, string password) { try diff --git a/song_of_the_day/MessengerIntegration/SignalIntegration.cs b/song_of_the_day/MessengerIntegration/SignalIntegration.cs index 077851e..dca7a55 100644 --- a/song_of_the_day/MessengerIntegration/SignalIntegration.cs +++ b/song_of_the_day/MessengerIntegration/SignalIntegration.cs @@ -1,29 +1,28 @@ -using System.Collections; -using System.ComponentModel; using song_of_the_day; public class SignalIntegration { - public static SignalIntegration? Instance; + private readonly ILogger logger; - private readonly ILogger logger; - - public SignalIntegration(string uri, int port, string phoneNumber) + public SignalIntegration(ILogger logger) { - using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Information)); - this.logger = factory.CreateLogger("SignalIntegration"); + + var uri = AppConfiguration.Instance.SignalAPIEndpointUri; + var port = int.Parse(AppConfiguration.Instance.SignalAPIEndpointPort); + var phoneNumber = AppConfiguration.Instance.HostPhoneNumber; + this.logger = logger; var http = new HttpClient() { BaseAddress = new Uri(uri + ":" + port) }; - apiClient = new song_of_the_day.swaggerClient(http); + apiClient = new swaggerClient(http); apiClient.BaseUrl = ""; this.phoneNumber = phoneNumber; } - private song_of_the_day.swaggerClient apiClient; + private swaggerClient apiClient; private string phoneNumber; @@ -83,6 +82,11 @@ public class SignalIntegration public async Task IntroduceUserAsync(User user) { + if (user == null || user.SignalMemberId == null) + { + logger.LogWarning("Attempt to introduce unknown user was aborted."); + return; + } await this.SendMessageToUserAsync("Hi, my name is Proggy and I am your friendly neighborhood *Song of the Day* bot!", user.SignalMemberId); await this.SendMessageToUserAsync("You are receiving this message because you have been invited to a *Song of the Day* community group.", user.SignalMemberId); await this.SendMessageToUserAsync("In that community group I will pick a person at random each day at 8 AM and encourage them to share a song with the rest of the community.", user.SignalMemberId); diff --git a/song_of_the_day/Pages/Shared/_Layout.cshtml b/song_of_the_day/Pages/Shared/_Layout.cshtml index 950fcca..681c919 100644 --- a/song_of_the_day/Pages/Shared/_Layout.cshtml +++ b/song_of_the_day/Pages/Shared/_Layout.cshtml @@ -3,8 +3,8 @@ { using (var dci = DataContext.Instance) { - var user = dci.Users.Where(u => u.LdapUserName == User.Identity.Name); - return user.Any(); + var user = dci.Users?.Where(u => u.LdapUserName == User.Identity.Name); + return user == null ? false : user.Any(); } } } @@ -18,7 +18,7 @@ - +
@@ -37,13 +37,15 @@ - @if (this.User.Identity.IsAuthenticated && !DoesUserHaveClaimedPhoneNumber()) + @if (this.User != null && this.User.Identity != null && + this.User.Identity.IsAuthenticated && !DoesUserHaveClaimedPhoneNumber()) { } - @if (this.User.Identity.IsAuthenticated && DoesUserHaveClaimedPhoneNumber()) + @if (this.User != null && this.User.Identity != null && + this.User.Identity.IsAuthenticated && DoesUserHaveClaimedPhoneNumber()) {