Compare commits
No commits in common. "49d8c2cb08eff55ae07a55adddfa990fb3cf38ef" and "6b9c3836975038579b07c516937c116e95c5211f" have entirely different histories.
49d8c2cb08
...
6b9c383697
15
HISTORY.md
15
HISTORY.md
@ -4,22 +4,11 @@ Changelog
|
|||||||
|
|
||||||
(unreleased)
|
(unreleased)
|
||||||
------------
|
------------
|
||||||
- Feat: add user management, refs NOISSUE. [Simon Diesenreiter]
|
|
||||||
- Feat(auth): initial auth added part 2, refs NOISSUE. [Simon
|
|
||||||
Diesenreiter]
|
|
||||||
- Feat(auth): initial auth added, refs NOISSUE. [Simon Diesenreiter]
|
|
||||||
|
|
||||||
|
|
||||||
0.1.21 (2025-04-15)
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
Fix
|
Fix
|
||||||
~~~
|
~~~
|
||||||
- Fix new user saving refs NOISSUE. [Simon Diesenreiter]
|
- Fix new user saving refs NOISSUE. [Simon Diesenreiter]
|
||||||
|
|
||||||
Other
|
|
||||||
~~~~~
|
|
||||||
|
|
||||||
|
|
||||||
0.1.20 (2025-04-15)
|
0.1.20 (2025-04-15)
|
||||||
-------------------
|
-------------------
|
||||||
@ -147,6 +136,10 @@ Other
|
|||||||
0.1.9 (2025-04-15)
|
0.1.9 (2025-04-15)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|
||||||
|
0.1.8 (2025-04-15)
|
||||||
|
------------------
|
||||||
|
|
||||||
Fix
|
Fix
|
||||||
~~~
|
~~~
|
||||||
- Additional debug outputs refs NOISSUE. [Simon Diesenreiter]
|
- Additional debug outputs refs NOISSUE. [Simon Diesenreiter]
|
||||||
|
@ -10,8 +10,7 @@ public class ConfigurationAD
|
|||||||
|
|
||||||
public string LDAPserver { get; set; } = string.Empty;
|
public string LDAPserver { get; set; } = string.Empty;
|
||||||
public string LDAPQueryBase { get; set; } = string.Empty;
|
public string LDAPQueryBase { get; set; } = string.Empty;
|
||||||
public string LDAPUserQueryBase { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
public string CrewGroup { get; set; } = string.Empty;
|
public string Crew { get; set; } = string.Empty;
|
||||||
public string ManagerGroup { get; set; } = string.Empty;
|
public string Managers { get; set; } = string.Empty;
|
||||||
}
|
}
|
@ -1,4 +0,0 @@
|
|||||||
public interface IAuthenticationService
|
|
||||||
{
|
|
||||||
bool Authenticate(string userName, string password);
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
public class LdapAuthenticationService : IAuthenticationService
|
|
||||||
{
|
|
||||||
private readonly IConfiguration _configuration;
|
|
||||||
|
|
||||||
public LdapAuthenticationService(IConfiguration configuration)
|
|
||||||
{
|
|
||||||
_configuration = configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Authenticate(string username, string password)
|
|
||||||
{
|
|
||||||
var ldapInstance = LdapIntegration.Instance;
|
|
||||||
return ldapInstance.TestLogin(username, password);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
public class PhoneClaimCodeProviderService
|
|
||||||
{
|
|
||||||
private Dictionary<string, string> _phoneClaimCodes;
|
|
||||||
private Dictionary<string, string> _phoneClaimNumbers;
|
|
||||||
|
|
||||||
public PhoneClaimCodeProviderService()
|
|
||||||
{
|
|
||||||
_phoneClaimCodes = new Dictionary<string, string>();
|
|
||||||
_phoneClaimNumbers = new Dictionary<string, string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Random random = new Random();
|
|
||||||
|
|
||||||
private static string RandomString(int length)
|
|
||||||
{
|
|
||||||
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
||||||
return new string(Enumerable.Repeat(chars, length)
|
|
||||||
.Select(s => s[random.Next(s.Length)]).ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GenerateClaimCodeForUserAndNumber(string username, string phoneNumber)
|
|
||||||
{
|
|
||||||
var generatedCode = string.Empty;
|
|
||||||
if (IsCodeGeneratedForUser(username))
|
|
||||||
{
|
|
||||||
generatedCode = _phoneClaimCodes[username];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
generatedCode = RandomString(6);
|
|
||||||
_phoneClaimCodes[username] = generatedCode;
|
|
||||||
_phoneClaimNumbers[username] = phoneNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
SignalIntegration.Instance.SendMessageToUserAsync("Your phone number validation code is: " + generatedCode, phoneNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ValidateClaimCodeForUser(string code, string username)
|
|
||||||
{
|
|
||||||
var result = false;
|
|
||||||
result = _phoneClaimCodes[username] == code;
|
|
||||||
|
|
||||||
if (result)
|
|
||||||
{
|
|
||||||
_phoneClaimCodes.Remove(username);
|
|
||||||
var number = _phoneClaimNumbers[username];
|
|
||||||
_phoneClaimNumbers.Remove(username);
|
|
||||||
return number;
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsCodeGeneratedForUser(string username)
|
|
||||||
{
|
|
||||||
return _phoneClaimCodes.ContainsKey(username);
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,21 +16,22 @@ public class AppConfiguration
|
|||||||
this.SignalGroupId = Environment.GetEnvironmentVariable("SIGNAL_GROUP_ID") ?? "group.Wmk1UTVQTnh0Sjd6a0xiOGhnTnMzZlNkc2p2Q3c0SXJiQkU2eDlNU0hyTT0=";
|
this.SignalGroupId = Environment.GetEnvironmentVariable("SIGNAL_GROUP_ID") ?? "group.Wmk1UTVQTnh0Sjd6a0xiOGhnTnMzZlNkc2p2Q3c0SXJiQkU2eDlNU0hyTT0=";
|
||||||
this.WebUIBaseURL = Environment.GetEnvironmentVariable("WEB_BASE_URL") ?? "https://sotd.disi.dev/";
|
this.WebUIBaseURL = Environment.GetEnvironmentVariable("WEB_BASE_URL") ?? "https://sotd.disi.dev/";
|
||||||
this.UseBotTag = bool.Parse(Environment.GetEnvironmentVariable("USE_BOT_TAG") ?? "true");
|
this.UseBotTag = bool.Parse(Environment.GetEnvironmentVariable("USE_BOT_TAG") ?? "true");
|
||||||
this.AverageDaysBetweenRequests = int.Parse(Environment.GetEnvironmentVariable("AVERAGE_DAYS_BETWEEN_REQUESTS") ?? "2");
|
|
||||||
var managersGroupName = Environment.GetEnvironmentVariable("LDAP_ADMINGROUP") ?? "admins";
|
var managersGroupName = Environment.GetEnvironmentVariable("LDAP_ADMINGROUP") ?? "admins";
|
||||||
var userGroupName = Environment.GetEnvironmentVariable("LDAP_USERGROUP") ?? "everybody";
|
var userGroupName = Environment.GetEnvironmentVariable("LDAP_USERGROUP") ?? "everyone";
|
||||||
this.LDAPConfig = new ConfigurationAD() {
|
this.LDAPConfig = new ConfigurationAD() {
|
||||||
Username = Environment.GetEnvironmentVariable("LDAP_BIND") ?? "cn=admin,dc=disi,dc=dev",
|
Username = Environment.GetEnvironmentVariable("LDAP_BIND") ?? "cn=admin.dc=disi,dc=dev",
|
||||||
Password = Environment.GetEnvironmentVariable("LDAP_PASS") ?? "adminPass2022!",
|
Password = Environment.GetEnvironmentVariable("LDAP_PASS") ?? "adminPass2022!",
|
||||||
Port = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("LDAP_BIND")) ? int.Parse(Environment.GetEnvironmentVariable("LDAP_BIND")) : 389,
|
Port = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("LDAP_BIND")) ? int.Parse(Environment.GetEnvironmentVariable("LDAP_BIND")) : 389,
|
||||||
LDAPserver = Environment.GetEnvironmentVariable("LDAP_URL") ?? "192.168.1.108",
|
LDAPserver = Environment.GetEnvironmentVariable("LDAP_URL") ?? "192.168.1.108",
|
||||||
LDAPQueryBase = Environment.GetEnvironmentVariable("LDAP_BASE") ?? "dc=disi,dc=dev",
|
LDAPQueryBase = Environment.GetEnvironmentVariable("LDAP_BASE") ?? "dc=disi,dc=dev",
|
||||||
LDAPUserQueryBase = Environment.GetEnvironmentVariable("LDAP_BASE") ?? "ou=people,dc=disi,dc=dev",
|
Crew = $"cn={userGroupName},ou=groups,dc=disi,dc=dev",
|
||||||
CrewGroup = $"cn={userGroupName},ou=groups,dc=disi,dc=dev",
|
Managers = $"cn={managersGroupName},ou=groups,dc=disi,dc=dev"
|
||||||
ManagerGroup = $"cn={managersGroupName},ou=groups,dc=disi,dc=dev"
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Crew { get; set; } = string.Empty;
|
||||||
|
public string Managers { get; set; } = string.Empty;
|
||||||
|
|
||||||
public string SignalAPIEndpointUri
|
public string SignalAPIEndpointUri
|
||||||
{
|
{
|
||||||
get; private set;
|
get; private set;
|
||||||
@ -86,11 +87,6 @@ public class AppConfiguration
|
|||||||
get; private set;
|
get; private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int AverageDaysBetweenRequests
|
|
||||||
{
|
|
||||||
get; private set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigurationAD LDAPConfig {
|
public ConfigurationAD LDAPConfig {
|
||||||
get; private set;
|
get; private set;
|
||||||
}
|
}
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Security.Claims;
|
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
|
|
||||||
public class AuthController : Controller
|
|
||||||
{
|
|
||||||
[HttpPost]
|
|
||||||
public async Task<IActionResult> Login(string username, string password)
|
|
||||||
{
|
|
||||||
var ldapService = HttpContext.RequestServices.GetService<LdapAuthenticationService>();
|
|
||||||
if (ldapService.Authenticate(username, password))
|
|
||||||
{
|
|
||||||
var claims = new[] { new Claim(ClaimTypes.Name, username) };
|
|
||||||
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
|
|
||||||
await HttpContext.SignInAsync(new ClaimsPrincipal(identity));
|
|
||||||
return RedirectToSamePageIfPossible();
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewBag.Error = "Invalid credentials";
|
|
||||||
return RedirectToSamePageIfPossible();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
public async Task<IActionResult> Logout()
|
|
||||||
{
|
|
||||||
await HttpContext.SignOutAsync();
|
|
||||||
return RedirectToSamePageIfPossible();
|
|
||||||
}
|
|
||||||
|
|
||||||
private IActionResult RedirectToSamePageIfPossible()
|
|
||||||
{
|
|
||||||
if (Request.Headers.ContainsKey("Referer"))
|
|
||||||
{
|
|
||||||
return Redirect(Request.Headers["Referer"].ToString());
|
|
||||||
}
|
|
||||||
return RedirectToPage("/");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,142 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace song_of_the_day.DataMigrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(DataContext))]
|
|
||||||
[Migration("20250516161831_UpdateUserModel")]
|
|
||||||
partial class UpdateUserModel
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasAnnotation("ProductVersion", "9.0.3")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Song", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("SongId")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("SongId"));
|
|
||||||
|
|
||||||
b.Property<string>("Artist")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Url")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("SongId");
|
|
||||||
|
|
||||||
b.ToTable("Songs");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("SongSuggestion", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime>("Date")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int?>("SongId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int?>("UserId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("SongId");
|
|
||||||
|
|
||||||
b.HasIndex("UserId");
|
|
||||||
|
|
||||||
b.ToTable("SongSuggestions");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("SuggestionHelper", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Title")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("SuggestionHelpers");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("User", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("UserId")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UserId"));
|
|
||||||
|
|
||||||
b.Property<bool>("AssociationInProgress")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<bool>("IsIntroduced")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("LdapUserName")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("NickName")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("SignalMemberId")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("UserId");
|
|
||||||
|
|
||||||
b.ToTable("Users");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("SongSuggestion", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Song", "Song")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("SongId");
|
|
||||||
|
|
||||||
b.HasOne("User", "User")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("UserId");
|
|
||||||
|
|
||||||
b.Navigation("Song");
|
|
||||||
|
|
||||||
b.Navigation("User");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,265 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace song_of_the_day.DataMigrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class UpdateUserModel : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropForeignKey(
|
|
||||||
name: "FK_SongSuggestions_Songs_SongId",
|
|
||||||
table: "SongSuggestions");
|
|
||||||
|
|
||||||
migrationBuilder.DropForeignKey(
|
|
||||||
name: "FK_SongSuggestions_Users_UserId",
|
|
||||||
table: "SongSuggestions");
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "SignalMemberId",
|
|
||||||
table: "Users",
|
|
||||||
type: "text",
|
|
||||||
nullable: true,
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "text");
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "NickName",
|
|
||||||
table: "Users",
|
|
||||||
type: "text",
|
|
||||||
nullable: true,
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "text");
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Name",
|
|
||||||
table: "Users",
|
|
||||||
type: "text",
|
|
||||||
nullable: true,
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "text");
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<bool>(
|
|
||||||
name: "AssociationInProgress",
|
|
||||||
table: "Users",
|
|
||||||
type: "boolean",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: false);
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "LdapUserName",
|
|
||||||
table: "Users",
|
|
||||||
type: "text",
|
|
||||||
nullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Title",
|
|
||||||
table: "SuggestionHelpers",
|
|
||||||
type: "text",
|
|
||||||
nullable: true,
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "text");
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Description",
|
|
||||||
table: "SuggestionHelpers",
|
|
||||||
type: "text",
|
|
||||||
nullable: true,
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "text");
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<int>(
|
|
||||||
name: "UserId",
|
|
||||||
table: "SongSuggestions",
|
|
||||||
type: "integer",
|
|
||||||
nullable: true,
|
|
||||||
oldClrType: typeof(int),
|
|
||||||
oldType: "integer");
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<int>(
|
|
||||||
name: "SongId",
|
|
||||||
table: "SongSuggestions",
|
|
||||||
type: "integer",
|
|
||||||
nullable: true,
|
|
||||||
oldClrType: typeof(int),
|
|
||||||
oldType: "integer");
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Url",
|
|
||||||
table: "Songs",
|
|
||||||
type: "text",
|
|
||||||
nullable: true,
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "text");
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Name",
|
|
||||||
table: "Songs",
|
|
||||||
type: "text",
|
|
||||||
nullable: true,
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "text");
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Artist",
|
|
||||||
table: "Songs",
|
|
||||||
type: "text",
|
|
||||||
nullable: true,
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "text");
|
|
||||||
|
|
||||||
migrationBuilder.AddForeignKey(
|
|
||||||
name: "FK_SongSuggestions_Songs_SongId",
|
|
||||||
table: "SongSuggestions",
|
|
||||||
column: "SongId",
|
|
||||||
principalTable: "Songs",
|
|
||||||
principalColumn: "SongId");
|
|
||||||
|
|
||||||
migrationBuilder.AddForeignKey(
|
|
||||||
name: "FK_SongSuggestions_Users_UserId",
|
|
||||||
table: "SongSuggestions",
|
|
||||||
column: "UserId",
|
|
||||||
principalTable: "Users",
|
|
||||||
principalColumn: "UserId");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropForeignKey(
|
|
||||||
name: "FK_SongSuggestions_Songs_SongId",
|
|
||||||
table: "SongSuggestions");
|
|
||||||
|
|
||||||
migrationBuilder.DropForeignKey(
|
|
||||||
name: "FK_SongSuggestions_Users_UserId",
|
|
||||||
table: "SongSuggestions");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "AssociationInProgress",
|
|
||||||
table: "Users");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "LdapUserName",
|
|
||||||
table: "Users");
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "SignalMemberId",
|
|
||||||
table: "Users",
|
|
||||||
type: "text",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: "",
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "text",
|
|
||||||
oldNullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "NickName",
|
|
||||||
table: "Users",
|
|
||||||
type: "text",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: "",
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "text",
|
|
||||||
oldNullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Name",
|
|
||||||
table: "Users",
|
|
||||||
type: "text",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: "",
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "text",
|
|
||||||
oldNullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Title",
|
|
||||||
table: "SuggestionHelpers",
|
|
||||||
type: "text",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: "",
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "text",
|
|
||||||
oldNullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Description",
|
|
||||||
table: "SuggestionHelpers",
|
|
||||||
type: "text",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: "",
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "text",
|
|
||||||
oldNullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<int>(
|
|
||||||
name: "UserId",
|
|
||||||
table: "SongSuggestions",
|
|
||||||
type: "integer",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: 0,
|
|
||||||
oldClrType: typeof(int),
|
|
||||||
oldType: "integer",
|
|
||||||
oldNullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<int>(
|
|
||||||
name: "SongId",
|
|
||||||
table: "SongSuggestions",
|
|
||||||
type: "integer",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: 0,
|
|
||||||
oldClrType: typeof(int),
|
|
||||||
oldType: "integer",
|
|
||||||
oldNullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Url",
|
|
||||||
table: "Songs",
|
|
||||||
type: "text",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: "",
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "text",
|
|
||||||
oldNullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Name",
|
|
||||||
table: "Songs",
|
|
||||||
type: "text",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: "",
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "text",
|
|
||||||
oldNullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Artist",
|
|
||||||
table: "Songs",
|
|
||||||
type: "text",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: "",
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "text",
|
|
||||||
oldNullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AddForeignKey(
|
|
||||||
name: "FK_SongSuggestions_Songs_SongId",
|
|
||||||
table: "SongSuggestions",
|
|
||||||
column: "SongId",
|
|
||||||
principalTable: "Songs",
|
|
||||||
principalColumn: "SongId",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
|
|
||||||
migrationBuilder.AddForeignKey(
|
|
||||||
name: "FK_SongSuggestions_Users_UserId",
|
|
||||||
table: "SongSuggestions",
|
|
||||||
column: "UserId",
|
|
||||||
principalTable: "Users",
|
|
||||||
principalColumn: "UserId",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -30,12 +30,15 @@ namespace song_of_the_day.DataMigrations
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("SongId"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("SongId"));
|
||||||
|
|
||||||
b.Property<string>("Artist")
|
b.Property<string>("Artist")
|
||||||
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<string>("Url")
|
b.Property<string>("Url")
|
||||||
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.HasKey("SongId");
|
b.HasKey("SongId");
|
||||||
@ -54,10 +57,10 @@ namespace song_of_the_day.DataMigrations
|
|||||||
b.Property<DateTime>("Date")
|
b.Property<DateTime>("Date")
|
||||||
.HasColumnType("timestamp with time zone");
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
b.Property<int?>("SongId")
|
b.Property<int>("SongId")
|
||||||
.HasColumnType("integer");
|
.HasColumnType("integer");
|
||||||
|
|
||||||
b.Property<int?>("UserId")
|
b.Property<int>("UserId")
|
||||||
.HasColumnType("integer");
|
.HasColumnType("integer");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
@ -78,9 +81,11 @@ namespace song_of_the_day.DataMigrations
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<string>("Description")
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<string>("Title")
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
@ -96,22 +101,19 @@ namespace song_of_the_day.DataMigrations
|
|||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UserId"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UserId"));
|
||||||
|
|
||||||
b.Property<bool>("AssociationInProgress")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<bool>("IsIntroduced")
|
b.Property<bool>("IsIntroduced")
|
||||||
.HasColumnType("boolean");
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
b.Property<string>("LdapUserName")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<string>("NickName")
|
b.Property<string>("NickName")
|
||||||
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<string>("SignalMemberId")
|
b.Property<string>("SignalMemberId")
|
||||||
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.HasKey("UserId");
|
b.HasKey("UserId");
|
||||||
@ -123,11 +125,15 @@ namespace song_of_the_day.DataMigrations
|
|||||||
{
|
{
|
||||||
b.HasOne("Song", "Song")
|
b.HasOne("Song", "Song")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("SongId");
|
.HasForeignKey("SongId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("User", "User")
|
b.HasOne("User", "User")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId");
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
b.Navigation("Song");
|
b.Navigation("Song");
|
||||||
|
|
||||||
|
@ -7,6 +7,4 @@ public class User
|
|||||||
public string? Name { get; set; }
|
public string? Name { get; set; }
|
||||||
public string? NickName { get; set; }
|
public string? NickName { get; set; }
|
||||||
public bool IsIntroduced { get; set; }
|
public bool IsIntroduced { get; set; }
|
||||||
public bool AssociationInProgress { get; set; }
|
|
||||||
public string? LdapUserName { get; set; }
|
|
||||||
}
|
}
|
@ -1,7 +0,0 @@
|
|||||||
public class LdapUser
|
|
||||||
{
|
|
||||||
public string? UserId { get; set; }
|
|
||||||
public string? FirstName { get; set; }
|
|
||||||
public string? LastName { get; set; }
|
|
||||||
public string? Email { get; set; }
|
|
||||||
}
|
|
@ -1,124 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using song_of_the_day;
|
|
||||||
using System.DirectoryServices.Protocols;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
public class LdapIntegration
|
|
||||||
{
|
|
||||||
public static LdapIntegration? Instance;
|
|
||||||
|
|
||||||
private readonly string[] attributesToQuery = new string[]
|
|
||||||
{
|
|
||||||
"uid",
|
|
||||||
"givenName",
|
|
||||||
"sn",
|
|
||||||
"mail"
|
|
||||||
};
|
|
||||||
|
|
||||||
public LdapIntegration(string uri, int port, string adminBind, string adminPass)
|
|
||||||
{
|
|
||||||
this.Uri = uri;
|
|
||||||
this.Port = port;
|
|
||||||
this.AdminBind = adminBind;
|
|
||||||
this.AdminPass = adminPass;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Uri { get; set; }
|
|
||||||
|
|
||||||
private int Port { get; set; }
|
|
||||||
|
|
||||||
private string AdminBind { get; set; }
|
|
||||||
|
|
||||||
private string AdminPass { get; set; }
|
|
||||||
|
|
||||||
public bool TestLogin(string username, string password)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var userList = this.SearchInADAsUser(
|
|
||||||
AppConfiguration.Instance.LDAPConfig.LDAPQueryBase,
|
|
||||||
$"(uid={username})",
|
|
||||||
SearchScope.Subtree,
|
|
||||||
username,
|
|
||||||
password);
|
|
||||||
}
|
|
||||||
catch (LdapException ex)
|
|
||||||
{
|
|
||||||
if (ex.Message.Contains("credential is invalid"))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<LdapUser> SearchInAD(
|
|
||||||
string targetOU,
|
|
||||||
string query,
|
|
||||||
SearchScope scope
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// search as admin
|
|
||||||
return this.SearchInADAsUser(targetOU, query, scope, this.AdminBind, this.AdminPass);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<LdapUser> SearchInADAsUser(
|
|
||||||
string targetOU,
|
|
||||||
string query,
|
|
||||||
SearchScope scope,
|
|
||||||
string userName,
|
|
||||||
string userPass
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// on Windows the authentication type is Negotiate, so there is no need to prepend
|
|
||||||
// AD user login with domain. On other platforms at the moment only
|
|
||||||
// Basic authentication is supported
|
|
||||||
var authType = AuthType.Basic;
|
|
||||||
|
|
||||||
var user = userName.StartsWith("cn=") || userName.StartsWith("uid=") ? userName : "uid=" + userName + "," + AppConfiguration.Instance.LDAPConfig.LDAPUserQueryBase;
|
|
||||||
|
|
||||||
//var connection = new LdapConnection(ldapServer)
|
|
||||||
var connection = new LdapConnection(
|
|
||||||
new LdapDirectoryIdentifier(this.Uri, this.Port)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
AuthType = authType,
|
|
||||||
Credential = new(user, userPass)
|
|
||||||
};
|
|
||||||
|
|
||||||
// the default one is v2 (at least in that version), and it is unknown if v3
|
|
||||||
// is actually needed, but at least Synology LDAP works only with v3,
|
|
||||||
// and since our Exchange doesn't complain, let it be v3
|
|
||||||
connection.SessionOptions.ProtocolVersion = 3;
|
|
||||||
|
|
||||||
// this is for connecting via LDAPS (636 port). It should be working,
|
|
||||||
// according to https://github.com/dotnet/runtime/issues/43890,
|
|
||||||
// but it doesn't (at least with Synology DSM LDAP), although perhaps
|
|
||||||
// for a different reason
|
|
||||||
//connection.SessionOptions.SecureSocketLayer = true;
|
|
||||||
|
|
||||||
connection.Bind();
|
|
||||||
|
|
||||||
var request = new SearchRequest(targetOU, query, scope, attributesToQuery);
|
|
||||||
|
|
||||||
var response = (System.DirectoryServices.Protocols.SearchResponse)connection.SendRequest(request);
|
|
||||||
|
|
||||||
var userList = new List<LdapUser>();
|
|
||||||
|
|
||||||
foreach(SearchResultEntry result in response.Entries)
|
|
||||||
{
|
|
||||||
userList.Add(new LdapUser() {
|
|
||||||
UserId = result.Attributes["uid"][0].ToString(),
|
|
||||||
FirstName = result.Attributes["givenName"][0].ToString(),
|
|
||||||
LastName = result.Attributes["sn"][0].ToString(),
|
|
||||||
Email = result.Attributes["mail"][0].ToString(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
connection.Dispose();
|
|
||||||
|
|
||||||
return userList;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +1,4 @@
|
|||||||
@{
|
<!DOCTYPE html>
|
||||||
bool DoesUserHaveClaimedPhoneNumber()
|
|
||||||
{
|
|
||||||
using (var dci = DataContext.Instance)
|
|
||||||
{
|
|
||||||
var user = dci.Users.Where(u => u.LdapUserName == User.Identity.Name);
|
|
||||||
return user.Any();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
@ -37,18 +26,9 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link text-dark" asp-area="" asp-page="/SuggestionHelpers">Suggestion Helpers</a>
|
<a class="nav-link text-dark" asp-area="" asp-page="/SuggestionHelpers">Suggestion Helpers</a>
|
||||||
</li>
|
</li>
|
||||||
@if (this.User.Identity.IsAuthenticated && !DoesUserHaveClaimedPhoneNumber())
|
|
||||||
{
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link text-dark" asp-area="" asp-page="/UnclaimedPhoneNumbers">Unclaimed Phone Numbers</a>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container" style="min-height: auto; width: 400px;">
|
|
||||||
<partial name="_LoginView" />
|
|
||||||
</div>
|
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
@using Microsoft.AspNetCore.Authentication
|
|
||||||
|
|
||||||
<div class="loginform">
|
|
||||||
@if (!this.User.Identity.IsAuthenticated)
|
|
||||||
{
|
|
||||||
<form method="post" action="Auth/Login">
|
|
||||||
<div>
|
|
||||||
<label for="username">Username:</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input name="username" type="text" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="password">Password:</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input name="password" type="password" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input name="submit" type="submit" value="Login" />
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<form method="post" action="Auth/Logout">
|
|
||||||
<div>
|
|
||||||
Welcome, @User.Identity.Name!
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input name="submit" type="submit" value="Logout" />
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
|
@ -1,38 +0,0 @@
|
|||||||
@page
|
|
||||||
@model UnclaimedPhoneNumbersModel
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Unclaimed Phone Numbers";
|
|
||||||
|
|
||||||
var codeService = HttpContext.RequestServices.GetService<PhoneClaimCodeProviderService>();
|
|
||||||
var codeGenerated = codeService.IsCodeGeneratedForUser(User.Identity.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
<div class="text-left">
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>Phone Number</th>
|
|
||||||
<th>Claim</th>
|
|
||||||
</tr>
|
|
||||||
@foreach (var user in @Model.UnclaimedUsers)
|
|
||||||
{
|
|
||||||
var phone = user.SignalMemberId; var userId = user.UserId;
|
|
||||||
<tr>
|
|
||||||
<td>@phone</td>
|
|
||||||
<td>
|
|
||||||
<form method="post">
|
|
||||||
<input name="userIndex" value="@userId" type="hidden" />
|
|
||||||
<input type="submit" title="Claim" value="Claim" disabled="@codeGenerated" />
|
|
||||||
</form>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</table>
|
|
||||||
@if(codeGenerated)
|
|
||||||
{
|
|
||||||
<form method="post">
|
|
||||||
<label for="code">Verification code:</label>
|
|
||||||
<input type="text" id="code" name="code" />
|
|
||||||
<input type="submit" title="Verify" value="Verify" asp-page-handler="SubmitCode" />
|
|
||||||
</form>
|
|
||||||
}
|
|
||||||
</div>
|
|
@ -1,64 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
|
||||||
using Microsoft.VisualBasic;
|
|
||||||
|
|
||||||
namespace sotd.Pages;
|
|
||||||
|
|
||||||
public class UnclaimedPhoneNumbersModel : PageModel
|
|
||||||
{
|
|
||||||
private readonly ILogger<UserModel> _logger;
|
|
||||||
|
|
||||||
public UnclaimedPhoneNumbersModel(ILogger<UserModel> logger)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int userId { get; set; }
|
|
||||||
|
|
||||||
[BindProperty]
|
|
||||||
public List<User> UnclaimedUsers { get; set; }
|
|
||||||
|
|
||||||
public void OnGet()
|
|
||||||
{
|
|
||||||
using (var dci = DataContext.Instance)
|
|
||||||
{
|
|
||||||
this.UnclaimedUsers = dci.Users.Where(u => string.IsNullOrEmpty(u.LdapUserName)).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnPost(int userIndex)
|
|
||||||
{
|
|
||||||
using (var dci = DataContext.Instance)
|
|
||||||
{
|
|
||||||
var user = dci.Users.Find(userIndex);
|
|
||||||
var claimCodeService = HttpContext.RequestServices.GetService<PhoneClaimCodeProviderService>();
|
|
||||||
claimCodeService.GenerateClaimCodeForUserAndNumber(HttpContext.User.Identity.Name, user.SignalMemberId);
|
|
||||||
this.UnclaimedUsers = dci.Users.Where(u => string.IsNullOrEmpty(u.LdapUserName)).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IActionResult OnPostSubmitCode(string code)
|
|
||||||
{
|
|
||||||
var claimCodeService = HttpContext.RequestServices.GetService<PhoneClaimCodeProviderService>();
|
|
||||||
var validatedNumber = claimCodeService.ValidateClaimCodeForUser(code, HttpContext.User.Identity.Name);
|
|
||||||
if (!string.IsNullOrEmpty(validatedNumber))
|
|
||||||
{
|
|
||||||
using (var dci = DataContext.Instance)
|
|
||||||
{
|
|
||||||
var user = dci.Users.Where(u => u.SignalMemberId == validatedNumber).FirstOrDefault();
|
|
||||||
if (user == default(User))
|
|
||||||
{
|
|
||||||
throw new Exception("User with specified phone number not found!");
|
|
||||||
}
|
|
||||||
user.LdapUserName = HttpContext.User.Identity.Name;
|
|
||||||
dci.SaveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new Exception("Invalid code provided!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return RedirectToPage("/");
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,17 +5,11 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using System.DirectoryServices.Protocols;
|
using System.DirectoryServices.Protocols;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
SignalIntegration.Instance = new SignalIntegration(AppConfiguration.Instance.SignalAPIEndpointUri,
|
SignalIntegration.Instance = new SignalIntegration(AppConfiguration.Instance.SignalAPIEndpointUri,
|
||||||
int.Parse(AppConfiguration.Instance.SignalAPIEndpointPort),
|
int.Parse(AppConfiguration.Instance.SignalAPIEndpointPort),
|
||||||
AppConfiguration.Instance.HostPhoneNumber);
|
AppConfiguration.Instance.HostPhoneNumber);
|
||||||
|
|
||||||
LdapIntegration.Instance = new LdapIntegration(AppConfiguration.Instance.LDAPConfig.LDAPserver,
|
|
||||||
AppConfiguration.Instance.LDAPConfig.Port,
|
|
||||||
AppConfiguration.Instance.LDAPConfig.Username,
|
|
||||||
AppConfiguration.Instance.LDAPConfig.Password);
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
Console.WriteLine("Setting up user check timer");
|
Console.WriteLine("Setting up user check timer");
|
||||||
@ -40,9 +34,7 @@ userCheckTimer.OnOccurence += async (s, ea) =>
|
|||||||
Name = newUserContact.Name,
|
Name = newUserContact.Name,
|
||||||
SignalMemberId = memberId,
|
SignalMemberId = memberId,
|
||||||
NickName = string.Empty,
|
NickName = string.Empty,
|
||||||
IsIntroduced = false,
|
IsIntroduced = false
|
||||||
LdapUserName = string.Empty,
|
|
||||||
AssociationInProgress = false,
|
|
||||||
};
|
};
|
||||||
dci.Users.Add(newUser);
|
dci.Users.Add(newUser);
|
||||||
needsSaving = true;
|
needsSaving = true;
|
||||||
@ -55,7 +47,7 @@ userCheckTimer.OnOccurence += async (s, ea) =>
|
|||||||
}
|
}
|
||||||
await dci.DisposeAsync();
|
await dci.DisposeAsync();
|
||||||
};
|
};
|
||||||
userCheckTimer.Start();
|
//userCheckTimer.Start();
|
||||||
|
|
||||||
Console.WriteLine("Setting up user intro timer");
|
Console.WriteLine("Setting up user intro timer");
|
||||||
var userIntroTimer = new CronTimer("*/1 * * * *", "Europe/Vienna", includingSeconds: false);
|
var userIntroTimer = new CronTimer("*/1 * * * *", "Europe/Vienna", includingSeconds: false);
|
||||||
@ -77,23 +69,13 @@ userIntroTimer.OnOccurence += async (s, ea) =>
|
|||||||
}
|
}
|
||||||
await dci.DisposeAsync();
|
await dci.DisposeAsync();
|
||||||
};
|
};
|
||||||
userIntroTimer.Start();
|
//userIntroTimer.Start();
|
||||||
|
|
||||||
|
|
||||||
Console.WriteLine("Setting up pick of the day timer");
|
Console.WriteLine("Setting up pick of the day timer");
|
||||||
var pickOfTheDayTimer = new CronTimer("0 8 * * *", "Europe/Vienna", includingSeconds: false);
|
var pickOfTheDayTimer = new CronTimer("0 8 * * *", "Europe/Vienna", includingSeconds: false);
|
||||||
pickOfTheDayTimer.OnOccurence += async (s, ea) =>
|
pickOfTheDayTimer.OnOccurence += async (s, ea) =>
|
||||||
{
|
{
|
||||||
var rand = new Random();
|
|
||||||
var num = rand.NextInt64();
|
|
||||||
var mod = num % AppConfiguration.Instance.AverageDaysBetweenRequests;
|
|
||||||
|
|
||||||
if (mod > 0)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Skipping pick of the day today!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var dci = DataContext.Instance;
|
var dci = DataContext.Instance;
|
||||||
var luckyUser = await dci.Users.ElementAtAsync((new Random()).Next(await dci.Users.CountAsync()));
|
var luckyUser = await dci.Users.ElementAtAsync((new Random()).Next(await dci.Users.CountAsync()));
|
||||||
var userName = string.IsNullOrEmpty(luckyUser.NickName) ? luckyUser.Name : luckyUser.NickName;
|
var userName = string.IsNullOrEmpty(luckyUser.NickName) ? luckyUser.Name : luckyUser.NickName;
|
||||||
@ -103,57 +85,88 @@ pickOfTheDayTimer.OnOccurence += async (s, ea) =>
|
|||||||
SignalIntegration.Instance.SendMessageToUserAsync($"Today's (optional) suggestion helper to help you pick a song is:\n\n**{suggestion.Title}**\n\n*{suggestion.Description}*", luckyUser.SignalMemberId);
|
SignalIntegration.Instance.SendMessageToUserAsync($"Today's (optional) suggestion helper to help you pick a song is:\n\n**{suggestion.Title}**\n\n*{suggestion.Description}*", luckyUser.SignalMemberId);
|
||||||
SignalIntegration.Instance.SendMessageToUserAsync($"For now please just share your suggestion with the group - in the future I might ask you to share directly with me or via the website to help me keep track of past suggestions!", luckyUser.SignalMemberId);
|
SignalIntegration.Instance.SendMessageToUserAsync($"For now please just share your suggestion with the group - in the future I might ask you to share directly with me or via the website to help me keep track of past suggestions!", luckyUser.SignalMemberId);
|
||||||
};
|
};
|
||||||
pickOfTheDayTimer.Start();
|
//pickOfTheDayTimer.Start();
|
||||||
|
|
||||||
var startUserAssociationProcess = (User userToAssociate) =>
|
var connection = new LdapConnection(AppConfiguration.Instance.LDAPConfig.LDAPserver)
|
||||||
{
|
{
|
||||||
SignalIntegration.Instance.SendMessageToUserAsync($"Hi, I see you are not associated with any website user yet.", userToAssociate.SignalMemberId);
|
Credential = new(
|
||||||
SignalIntegration.Instance.SendMessageToUserAsync($"If you haven't yet, please navigate to https://users.disi.dev to create a new account.", userToAssociate.SignalMemberId);
|
AppConfiguration.Instance.LDAPConfig.Username,
|
||||||
SignalIntegration.Instance.SendMessageToUserAsync($"Once you have done so, go to https://sotd.disi.dev, login, navigate to \"Unclaimed Phone Numbers\" and click on the \"Claim\" button to start the claim process.", userToAssociate.SignalMemberId);
|
AppConfiguration.Instance.LDAPConfig.Password
|
||||||
SignalIntegration.Instance.SendMessageToUserAsync($"With a future update you will be required to submit songs via your user account - at that point you will be skipped during the selection process if you have not yet claimed your phone number!", userToAssociate.SignalMemberId);
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
Console.WriteLine("Setting up LdapAssociation timer");
|
var attributesToQuery = new string[]
|
||||||
var ldapAssociationTimer = new CronTimer("*/10 * * * *", "Europe/Vienna", includingSeconds: false);
|
|
||||||
ldapAssociationTimer.OnOccurence += async (s, ea) =>
|
|
||||||
{
|
{
|
||||||
var dci = DataContext.Instance;
|
"objectGUID",
|
||||||
var nonAssociatedUsers = dci.Users.Where(u => string.IsNullOrEmpty(u.LdapUserName) && !u.AssociationInProgress);
|
"sAMAccountName",
|
||||||
var needsSaving = false;
|
"displayName",
|
||||||
foreach (var user in nonAssociatedUsers)
|
"mail",
|
||||||
{
|
"whenCreated"
|
||||||
user.AssociationInProgress = true;
|
|
||||||
|
|
||||||
startUserAssociationProcess(user);
|
|
||||||
user.IsIntroduced = true;
|
|
||||||
needsSaving = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needsSaving)
|
|
||||||
{
|
|
||||||
await dci.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
await dci.DisposeAsync();
|
|
||||||
};
|
};
|
||||||
ldapAssociationTimer.Start();
|
|
||||||
|
|
||||||
var searchResults = LdapIntegration.Instance.SearchInAD(
|
SearchResponse SearchInAD(
|
||||||
|
string ldapServer,
|
||||||
|
int ldapPort,
|
||||||
|
string domainForAD,
|
||||||
|
string username,
|
||||||
|
string password,
|
||||||
|
string targetOU,
|
||||||
|
string query,
|
||||||
|
SearchScope scope,
|
||||||
|
params string[] attributeList
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// on Windows the authentication type is Negotiate, so there is no need to prepend
|
||||||
|
// AD user login with domain. On other platforms at the moment only
|
||||||
|
// Basic authentication is supported
|
||||||
|
var authType = AuthType.Basic;
|
||||||
|
|
||||||
|
//var connection = new LdapConnection(ldapServer)
|
||||||
|
var connection = new LdapConnection(
|
||||||
|
new LdapDirectoryIdentifier(ldapServer, ldapPort)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
AuthType = authType,
|
||||||
|
Credential = new(username, password)
|
||||||
|
};
|
||||||
|
// the default one is v2 (at least in that version), and it is unknown if v3
|
||||||
|
// is actually needed, but at least Synology LDAP works only with v3,
|
||||||
|
// and since our Exchange doesn't complain, let it be v3
|
||||||
|
connection.SessionOptions.ProtocolVersion = 3;
|
||||||
|
|
||||||
|
// this is for connecting via LDAPS (636 port). It should be working,
|
||||||
|
// according to https://github.com/dotnet/runtime/issues/43890,
|
||||||
|
// but it doesn't (at least with Synology DSM LDAP), although perhaps
|
||||||
|
// for a different reason
|
||||||
|
//connection.SessionOptions.SecureSocketLayer = true;
|
||||||
|
|
||||||
|
connection.Bind();
|
||||||
|
|
||||||
|
var request = new SearchRequest(targetOU, query, scope, attributeList);
|
||||||
|
|
||||||
|
return (SearchResponse)connection.SendRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
var searchResults = SearchInAD(
|
||||||
|
AppConfiguration.Instance.LDAPConfig.LDAPserver,
|
||||||
|
AppConfiguration.Instance.LDAPConfig.Port,
|
||||||
|
AppConfiguration.Instance.LDAPConfig.Username,
|
||||||
|
AppConfiguration.Instance.LDAPConfig.Password,
|
||||||
AppConfiguration.Instance.LDAPConfig.LDAPQueryBase,
|
AppConfiguration.Instance.LDAPConfig.LDAPQueryBase,
|
||||||
$"(memberOf={AppConfiguration.Instance.LDAPConfig.CrewGroup})",
|
new StringBuilder("(&")
|
||||||
SearchScope.Subtree
|
.Append("(objectCategory=person)")
|
||||||
|
.Append("(objectClass=user)")
|
||||||
|
.Append($"(memberOf={_configurationAD.Crew})")
|
||||||
|
.Append("(!(userAccountControl:1.2.840.113556.1.4.803:=2))")
|
||||||
|
.Append(")")
|
||||||
|
.ToString(),
|
||||||
|
SearchScope.Subtree,
|
||||||
|
attributesToQuery
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
builder.Services.AddRazorPages();
|
builder.Services.AddRazorPages();
|
||||||
builder.Services.AddOpenApi();
|
builder.Services.AddOpenApi();
|
||||||
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
|
||||||
.AddCookie(options =>
|
|
||||||
{
|
|
||||||
options.LoginPath = "/Auth/Login";
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.Services.AddSingleton<LdapAuthenticationService>();
|
|
||||||
builder.Services.AddSingleton<PhoneClaimCodeProviderService>();
|
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
@ -175,15 +188,5 @@ app.UseAuthorization();
|
|||||||
app.MapStaticAssets();
|
app.MapStaticAssets();
|
||||||
app.MapRazorPages()
|
app.MapRazorPages()
|
||||||
.WithStaticAssets();
|
.WithStaticAssets();
|
||||||
app.MapControllerRoute(
|
|
||||||
name: "login",
|
|
||||||
pattern: "{controller=Auth}/{action=Login}"
|
|
||||||
);
|
|
||||||
app.MapControllerRoute(
|
|
||||||
name: "logout",
|
|
||||||
pattern: "{controller=Auth}/{action=Logout}"
|
|
||||||
);
|
|
||||||
app.MapGet("/debug/routes", (IEnumerable<EndpointDataSource> endpointSources) =>
|
|
||||||
string.Join("\n", endpointSources.SelectMany(source => source.Endpoints)));
|
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
@ -1 +1 @@
|
|||||||
0.2.0
|
0.1.21
|
||||||
|
Loading…
x
Reference in New Issue
Block a user