Compare commits

..

15 Commits
0.9.2 ... 0.9.3

Author SHA1 Message Date
Leendert de Borst
70220cecbb Merge pull request #466 from lanedirt/465-prepare-093-release
Update version to 0.9.3
2024-12-13 13:09:33 +01:00
Leendert de Borst
c63faa352f Update version to 0.9.3 (#465) 2024-12-13 13:09:14 +01:00
Leendert de Borst
7e261a05c9 Merge pull request #464 from lanedirt/463-bump-spamokpasswordgenerator-library-to-110
Bump SpamOK.PasswordGenerator version to 1.1.0
2024-12-13 13:01:45 +01:00
Leendert de Borst
545ec5576e Bump SpamOK.PasswordGenerator version to 1.1.0 (#463) 2024-12-13 12:45:05 +01:00
dependabot[bot]
73dcbe5860 Bump the npm_and_yarn group across 2 directories with 1 update
Bumps the npm_and_yarn group with 1 update in the /src/AliasVault.Admin directory: [nanoid](https://github.com/ai/nanoid).
Bumps the npm_and_yarn group with 1 update in the /src/AliasVault.Client directory: [nanoid](https://github.com/ai/nanoid).


Updates `nanoid` from 3.3.7 to 3.3.8
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.3.7...3.3.8)

Updates `nanoid` from 3.3.7 to 3.3.8
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.3.7...3.3.8)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: nanoid
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-13 12:15:20 +01:00
Leendert de Borst
13917444b9 Merge pull request #461 from lanedirt/460-password-not-correct
Fix account registration username capitalization login bug
2024-12-13 12:15:08 +01:00
dependabot[bot]
119e13a9dd Bump Serilog from 4.1.0 to 4.2.0
Bumps [Serilog](https://github.com/serilog/serilog) from 4.1.0 to 4.2.0.
- [Release notes](https://github.com/serilog/serilog/releases)
- [Commits](https://github.com/serilog/serilog/compare/v4.1.0...v4.2.0)

---
updated-dependencies:
- dependency-name: Serilog
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-13 11:50:58 +01:00
dependabot[bot]
7d656e9a9a Bump Microsoft.IdentityModel.JsonWebTokens and Microsoft.IdentityModel.Tokens
Bumps [Microsoft.IdentityModel.JsonWebTokens](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) and [Microsoft.IdentityModel.Tokens](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet). These dependencies needed to be updated together.

Updates `Microsoft.IdentityModel.JsonWebTokens` from 8.2.1 to 8.3.0
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/8.2.1...8.3.0)

Updates `Microsoft.IdentityModel.Tokens` from 8.2.1 to 8.3.0
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/8.2.1...8.3.0)

---
updated-dependencies:
- dependency-name: Microsoft.IdentityModel.JsonWebTokens
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: Microsoft.IdentityModel.Tokens
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-13 11:50:50 +01:00
Leendert de Borst
8bd05b5c2e Fix account registration username capitalization login bug (#460) 2024-12-13 11:50:18 +01:00
Leendert de Borst
1e65f14323 Update README.md 2024-12-11 23:30:41 +01:00
Leendert de Borst
2c7543889d Update README.md 2024-12-11 18:24:10 +01:00
Leendert de Borst
63c5483208 Merge pull request #455 from lanedirt/454-update-default-server-settings
Update default server settings
2024-12-05 10:07:07 +01:00
Leendert de Borst
2586d61651 Merge pull request #457 from lanedirt/456-add-task-runner-to-installsh-pull-list
Update install.sh to include task runner to image pull list
2024-12-05 10:07:00 +01:00
Leendert de Borst
c7a32cf0e9 Update install.sh (#456) 2024-12-04 21:32:13 +01:00
Leendert de Borst
46cc6527aa Update default server settings (#454) 2024-12-04 19:17:56 +01:00
12 changed files with 154 additions and 33 deletions

View File

@@ -6,9 +6,9 @@
<a href="https://app.aliasvault.net">Live demo 🔥</a> • <a href="https://aliasvault.net?utm_source=gh-readme">Website 🌐</a> • <a href="https://docs.aliasvault.net?utm_source=gh-readme">Documentation 📚</a> • <a href="#installation">Installation ⚙️</a>
</p>
<h3 align="center">
Open-source password and alias manager
</h3>
<p align="center">
<strong>Open-source password and alias manager</strong>
</p>
[<img src="https://img.shields.io/github/v/release/lanedirt/AliasVault?include_prereleases&logo=github">](https://github.com/lanedirt/AliasVault/releases)
[<img src="https://img.shields.io/github/actions/workflow/status/lanedirt/AliasVault/docker-compose-build.yml?label=docker-compose%20build">](https://github.com/lanedirt/AliasVault/actions/workflows/docker-compose-build.yml)
@@ -25,7 +25,7 @@ Open-source password and alias manager
</div>
AliasVault is an open-source password and alias manager built with C# ASP.NET technology. AliasVault can be self-hosted on your own server with Docker, providing a secure and private solution for managing your online identities and passwords.
AliasVault is an end-to-end encrypted password and alias manager that protects your privacy by creating alternative identities, passwords and email addresses for every website you use. The core of AliasVault is built with C# ASP.NET Blazor WASM technology. AliasVault can be self-hosted on your own server with Docker.
### What makes AliasVault unique:
- **Zero-knowledge architecture**: All data is end-to-end encrypted on the client and stored in encrypted state on the server. Your master password never leaves your device and the server never has access to your data.

View File

@@ -1,5 +1,5 @@
#!/bin/bash
# @version 0.9.0
# @version 0.9.3
# Repository information used for downloading files and images from GitHub
REPO_OWNER="lanedirt"
@@ -1295,6 +1295,7 @@ handle_install_version() {
"${GITHUB_CONTAINER_REGISTRY}-client:${target_version}"
"${GITHUB_CONTAINER_REGISTRY}-admin:${target_version}"
"${GITHUB_CONTAINER_REGISTRY}-smtp:${target_version}"
"${GITHUB_CONTAINER_REGISTRY}-task-runner:${target_version}"
)
for image in "${images[@]}"; do

View File

@@ -710,9 +710,9 @@
}
},
"node_modules/nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"version": "3.3.8",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"funding": [
{
"type": "github",

View File

@@ -23,8 +23,8 @@
<PackageReference Include="Asp.Versioning.Mvc" Version="8.1.0" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.0" />
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.2.1" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.2.1" />
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.3.0" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.3.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -710,9 +710,9 @@
}
},
"node_modules/nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"version": "3.3.8",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"funding": [
{
"type": "github",

View File

@@ -24,7 +24,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="SpamOK.PasswordGenerator" Version="1.0.1" />
<PackageReference Include="SpamOK.PasswordGenerator" Version="1.1.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -30,7 +30,7 @@ public static class AppInfo
/// <summary>
/// Gets the patch version number.
/// </summary>
public const int VersionPatch = 2;
public const int VersionPatch = 3;
/// <summary>
/// Gets the build number, typically used in CI/CD pipelines.

View File

@@ -23,7 +23,7 @@ public class ServerSettingsModel
public int AuthLogRetentionDays { get; set; } = 30;
/// <summary>
/// Gets or sets the email retention days. Defaults to 0 (disabled).
/// Gets or sets the email retention days. Defaults to 0 (unlimited).
/// </summary>
public int EmailRetentionDays { get; set; }

View File

@@ -99,19 +99,53 @@ public class ServerSettingsService(IDbContextFactory<AliasServerDbContext> dbCon
await using var dbContext = await dbContextFactory.CreateDbContextAsync(CancellationToken.None);
var settings = await dbContext.ServerSettings.ToDictionaryAsync(x => x.Key, x => x.Value);
return new ServerSettingsModel
// Create model with defaults
var model = new ServerSettingsModel();
// Only override if parsing succeeds
if (int.TryParse(settings.GetValueOrDefault("GeneralLogRetentionDays"), out var generalDays))
{
GeneralLogRetentionDays = int.TryParse(settings.GetValueOrDefault("GeneralLogRetentionDays"), out var generalDays) ? generalDays : 30,
AuthLogRetentionDays = int.TryParse(settings.GetValueOrDefault("AuthLogRetentionDays"), out var authDays) ? authDays : 90,
EmailRetentionDays = int.TryParse(settings.GetValueOrDefault("EmailRetentionDays"), out var emailDays) ? emailDays : 30,
MaxEmailsPerUser = int.TryParse(settings.GetValueOrDefault("MaxEmailsPerUser"), out var maxEmails) ? maxEmails : 100,
MaintenanceTime = TimeOnly.TryParse(
settings.GetValueOrDefault("MaintenanceTime") ?? "00:00",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out var time) ? time : new TimeOnly(0, 0),
TaskRunnerDays = settings.GetValueOrDefault("TaskRunnerDays")?.Split(',').Select(int.Parse).ToList() ?? new List<int> { 1, 2, 3, 4, 5, 6, 7 },
};
model.GeneralLogRetentionDays = generalDays;
}
if (int.TryParse(settings.GetValueOrDefault("AuthLogRetentionDays"), out var authDays))
{
model.AuthLogRetentionDays = authDays;
}
if (int.TryParse(settings.GetValueOrDefault("EmailRetentionDays"), out var emailDays))
{
model.EmailRetentionDays = emailDays;
}
if (int.TryParse(settings.GetValueOrDefault("MaxEmailsPerUser"), out var maxEmails))
{
model.MaxEmailsPerUser = maxEmails;
}
if (TimeOnly.TryParse(
settings.GetValueOrDefault("MaintenanceTime") ?? "00:00",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out var time))
{
model.MaintenanceTime = time;
}
var taskRunnerDaysStr = settings.GetValueOrDefault("TaskRunnerDays");
if (!string.IsNullOrEmpty(taskRunnerDaysStr))
{
try
{
model.TaskRunnerDays = taskRunnerDaysStr.Split(',').Select(int.Parse).ToList();
}
catch (FormatException)
{
// Keep default if parsing fails
}
}
return model;
}
/// <summary>

View File

@@ -68,11 +68,49 @@ public class AuthTests : ClientPlaywrightTest
}
/// <summary>
/// Test if logging out and logging in works.
/// Test if logging in with different case variations of username works.
/// </summary>
/// <returns>Async task.</returns>
[Test]
[Order(3)]
public async Task CapitalizedUsernameTest()
{
// Logout current user
await Logout();
// Create a new user with capital letters in username
var capitalUsername = "TestUser@Example.com";
await Register(checkForSuccess: true, username: capitalUsername);
await Logout();
// Test Case 1: Try to login with lowercase version of the username
var lowercaseUsername = capitalUsername.ToLower();
await LoginWithUsername(lowercaseUsername);
await VerifySuccessfulLogin();
// Test Case 2: Try to login with exact capitalized username
await Logout();
await LoginWithUsername(capitalUsername);
await VerifySuccessfulLogin();
// Test Case 3: Create new user with lowercase
await Logout();
var lowercaseUser = "testuser2@example.com";
await Register(checkForSuccess: true, username: lowercaseUser);
await Logout();
// Try logging in with uppercase version
var uppercaseVersion = lowercaseUser.ToUpper();
await LoginWithUsername(uppercaseVersion);
await VerifySuccessfulLogin();
}
/// <summary>
/// Test if logging out and logging in works.
/// </summary>
/// <returns>Async task.</returns>
[Test]
[Order(4)]
public async Task LogoutAndLoginRememberMeTest()
{
await Logout();
@@ -101,7 +139,7 @@ public class AuthTests : ClientPlaywrightTest
/// </summary>
/// <returns>Async task.</returns>
[Test]
[Order(4)]
[Order(5)]
public async Task RegisterFormWarningTest()
{
await Logout();
@@ -116,7 +154,7 @@ public class AuthTests : ClientPlaywrightTest
/// </summary>
/// <returns>Async task.</returns>
[Test]
[Order(5)]
[Order(6)]
public async Task PasswordAuthLockoutTest()
{
await Logout();
@@ -152,4 +190,41 @@ public class AuthTests : ClientPlaywrightTest
var pageContent = await Page.TextContentAsync("body");
Assert.That(pageContent, Does.Contain("locked out"), "No account lockout message.");
}
/// <summary>
/// Login with a given username.
/// </summary>
/// <param name="username">The username to login with.</param>
/// <returns>Async task.</returns>
private async Task LoginWithUsername(string username)
{
await NavigateToLogin();
var emailField = await WaitForAndGetElement("input[id='email']");
var passwordField = await WaitForAndGetElement("input[id='password']");
await emailField.FillAsync(username);
await passwordField.FillAsync(TestUserPassword);
var loginButton = await WaitForAndGetElement("button[type='submit']");
await loginButton.ClickAsync();
}
/// <summary>
/// Verify that a login was successful.
/// </summary>
/// <returns>Async task.</returns>
private async Task VerifySuccessfulLogin()
{
// Wait for the index page to load which should show "Credentials" in the top menu.
await WaitForUrlAsync("**", "Credentials");
// Check if the login was successful by verifying content.
var pageContent = await Page.TextContentAsync("body");
Assert.That(pageContent, Does.Contain(WelcomeMessage), "No index content after logging in.");
// Check if login has created an auth log entry.
var authLogEntry = await ApiDbContext.AuthLogs.FirstOrDefaultAsync(x =>
x.EventType == AuthEventType.Login);
Assert.That(authLogEntry, Is.Not.Null, "Auth log entry not found in database after login.");
}
}

View File

@@ -23,7 +23,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Serilog" Version="4.1.0" />
<PackageReference Include="Serilog" Version="4.2.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.4" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />

View File

@@ -26,7 +26,9 @@ public static class Srp
/// <returns>SrpSignup model.</returns>
public static SrpPasswordChange PasswordChangeAsync(SrpClient client, string salt, string username, string passwordHashString)
{
// Derive a key from the password using Argon2id
// Derive a key from the password using Argon2id.
// Make sure the username is lowercase as the SRP protocol is case sensitive.
username = username.ToLowerInvariant();
// Signup or password change: client generates a salt and verifier.
var privateKey = DerivePrivateKey(salt, username, passwordHashString);
@@ -44,6 +46,9 @@ public static class Srp
/// <returns>Private key as string.</returns>
public static string DerivePrivateKey(string salt, string username, string passwordHashString)
{
// Make sure the username is lowercase as the SRP protocol is case sensitive.
username = username.ToLowerInvariant();
var client = new SrpClient();
return client.DerivePrivateKey(salt, username, passwordHashString);
}
@@ -80,6 +85,9 @@ public static class Srp
/// <returns>session.</returns>
public static SrpSession DeriveSessionClient(string privateKey, string clientSecretEphemeral, string serverEphemeralPublic, string salt, string username)
{
// Make sure the username is lowercase as the SRP protocol is case sensitive.
username = username.ToLowerInvariant();
var client = new SrpClient();
return client.DeriveSession(
clientSecretEphemeral,
@@ -101,6 +109,9 @@ public static class Srp
/// <returns>SrpSession.</returns>
public static SrpSession? DeriveSessionServer(string serverEphemeralSecret, string clientEphemeralPublic, string salt, string username, string verifier, string clientSessionProof)
{
// Make sure the username is lowercase as the SRP protocol is case sensitive.
username = username.ToLowerInvariant();
try
{
var server = new SrpServer();