mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Removing Identity Server and creating a custom jwt
This commit is contained in:
parent
425c80237b
commit
3962c0eb7a
@ -18,27 +18,21 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Autofac;
|
||||
using IdentityServer4.Extensions;
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Services;
|
||||
using Kyoo.Abstractions;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Authentication.Models;
|
||||
using Kyoo.Authentication.Views;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.IdentityModel.Logging;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using SameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode;
|
||||
|
||||
namespace Kyoo.Authentication
|
||||
@ -55,14 +49,13 @@ namespace Kyoo.Authentication
|
||||
public string Name => "Authentication";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Description => "Enable OpenID authentication for Kyoo.";
|
||||
public string Description => "Enable an authentication/permission system for Kyoo (via Jwt or ApKeys).";
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, Type> Configuration => new()
|
||||
{
|
||||
{ AuthenticationOption.Path, typeof(AuthenticationOption) },
|
||||
{ PermissionOption.Path, typeof(PermissionOption) },
|
||||
{ CertificateOption.Path, typeof(CertificateOption) }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@ -70,11 +63,6 @@ namespace Kyoo.Authentication
|
||||
/// </summary>
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
/// <summary>
|
||||
/// The logger used to allow IdentityServer to log things.
|
||||
/// </summary>
|
||||
private readonly ILogger<DefaultCorsPolicyService> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// The environment information to check if the app runs in debug mode
|
||||
/// </summary>
|
||||
@ -84,16 +72,11 @@ namespace Kyoo.Authentication
|
||||
/// Create a new authentication module instance and use the given configuration and environment.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The configuration to use</param>
|
||||
/// <param name="logger">The logger used to allow IdentityServer to log things</param>
|
||||
/// <param name="environment">The environment information to check if the app runs in debug mode</param>
|
||||
[SuppressMessage("ReSharper", "ContextualLoggerProblem",
|
||||
Justification = "The logger is used for a dependency that is not created via the container.")]
|
||||
public AuthenticationModule(IConfiguration configuration,
|
||||
ILogger<DefaultCorsPolicyService> logger,
|
||||
IWebHostEnvironment environment)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
_environment = environment;
|
||||
}
|
||||
|
||||
@ -101,59 +84,29 @@ namespace Kyoo.Authentication
|
||||
public void Configure(ContainerBuilder builder)
|
||||
{
|
||||
builder.RegisterType<PermissionValidator>().As<IPermissionValidator>().SingleInstance();
|
||||
|
||||
DefaultCorsPolicyService cors = new(_logger)
|
||||
{
|
||||
AllowedOrigins = { _configuration.GetPublicUrl().GetLeftPart(UriPartial.Authority) }
|
||||
};
|
||||
builder.RegisterInstance(cors).As<ICorsPolicyService>().SingleInstance();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Configure(IServiceCollection services)
|
||||
{
|
||||
Uri publicUrl = _configuration.GetPublicUrl();
|
||||
|
||||
if (_environment.IsDevelopment())
|
||||
IdentityModelEventSource.ShowPII = true;
|
||||
|
||||
services.AddControllers();
|
||||
AuthenticationOption jwt = new();
|
||||
_configuration.GetSection(AuthenticationOption.Path).Bind(jwt);
|
||||
|
||||
// TODO handle direct-videos with bearers (probably add a cookie and a app.Use to translate that for videos)
|
||||
// TODO Check if tokens should be stored.
|
||||
List<Client> clients = new();
|
||||
_configuration.GetSection("authentication:clients").Bind(clients);
|
||||
CertificateOption certificateOptions = new();
|
||||
_configuration.GetSection(CertificateOption.Path).Bind(certificateOptions);
|
||||
|
||||
clients.AddRange(IdentityContext.GetClients());
|
||||
foreach (Client client in clients)
|
||||
{
|
||||
client.RedirectUris = client.RedirectUris
|
||||
.Select(x => x.StartsWith("/") ? publicUrl.ToString().TrimEnd('/') + x : x)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
services.AddIdentityServer(options =>
|
||||
{
|
||||
options.IssuerUri = publicUrl.ToString();
|
||||
options.UserInteraction.LoginUrl = $"{publicUrl}login";
|
||||
options.UserInteraction.ErrorUrl = $"{publicUrl}error";
|
||||
options.UserInteraction.LogoutUrl = $"{publicUrl}logout";
|
||||
})
|
||||
.AddInMemoryIdentityResources(IdentityContext.GetIdentityResources())
|
||||
.AddInMemoryApiScopes(IdentityContext.GetScopes())
|
||||
.AddInMemoryApiResources(IdentityContext.GetApis())
|
||||
.AddInMemoryClients(clients)
|
||||
.AddProfileService<AccountApi>()
|
||||
.AddSigninKeys(certificateOptions);
|
||||
|
||||
services.AddAuthentication()
|
||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.Authority = publicUrl.ToString();
|
||||
options.Audience = "kyoo";
|
||||
options.RequireHttpsMetadata = false;
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidateAudience = true,
|
||||
ValidateLifetime = true,
|
||||
ValidateIssuerSigningKey = true,
|
||||
ValidIssuer = publicUrl.ToString(),
|
||||
ValidAudience = publicUrl.ToString(),
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwt.Secret))
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,137 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Kyoo.Authentication.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Org.BouncyCastle.Asn1.X509;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Generators;
|
||||
using Org.BouncyCastle.Crypto.Operators;
|
||||
using Org.BouncyCastle.Math;
|
||||
using Org.BouncyCastle.Pkcs;
|
||||
using Org.BouncyCastle.Security;
|
||||
using Org.BouncyCastle.Utilities;
|
||||
using Org.BouncyCastle.X509;
|
||||
using X509Certificate = Org.BouncyCastle.X509.X509Certificate;
|
||||
|
||||
namespace Kyoo.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing multiple extensions methods to manage certificates.
|
||||
/// </summary>
|
||||
public static class Certificates
|
||||
{
|
||||
/// <summary>
|
||||
/// Add the certificate file to the identity server. If the certificate will expire soon, automatically renew it.
|
||||
/// If no certificate exists, one is generated.
|
||||
/// </summary>
|
||||
/// <param name="builder">The identity server that will be modified.</param>
|
||||
/// <param name="options">The certificate options</param>
|
||||
/// <returns>The initial builder to allow chain-calls.</returns>
|
||||
public static IIdentityServerBuilder AddSigninKeys(this IIdentityServerBuilder builder,
|
||||
CertificateOption options)
|
||||
{
|
||||
X509Certificate2 certificate = _GetCertificate(options);
|
||||
builder.AddSigningCredential(certificate);
|
||||
|
||||
if (certificate.NotAfter.AddDays(-7) <= DateTime.UtcNow)
|
||||
{
|
||||
Console.WriteLine("Signin certificate will expire soon, renewing it.");
|
||||
if (File.Exists(options.OldFile))
|
||||
File.Delete(options.OldFile);
|
||||
File.Move(options.File, options.OldFile);
|
||||
builder.AddValidationKey(_GenerateCertificate(options.File, options.Password));
|
||||
}
|
||||
else if (File.Exists(options.OldFile))
|
||||
builder.AddValidationKey(_GetExistingCredential(options.OldFile, options.Password));
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or generate the sign-in certificate.
|
||||
/// </summary>
|
||||
/// <param name="options">The certificate options</param>
|
||||
/// <returns>A valid certificate</returns>
|
||||
private static X509Certificate2 _GetCertificate(CertificateOption options)
|
||||
{
|
||||
return File.Exists(options.File)
|
||||
? _GetExistingCredential(options.File, options.Password)
|
||||
: _GenerateCertificate(options.File, options.Password);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load a certificate from a file
|
||||
/// </summary>
|
||||
/// <param name="file">The path of the certificate</param>
|
||||
/// <param name="password">The password of the certificate</param>
|
||||
/// <returns>The loaded certificate</returns>
|
||||
private static X509Certificate2 _GetExistingCredential(string file, string password)
|
||||
{
|
||||
X509KeyStorageFlags storeFlags = X509KeyStorageFlags.MachineKeySet |
|
||||
X509KeyStorageFlags.PersistKeySet |
|
||||
X509KeyStorageFlags.Exportable;
|
||||
return new X509Certificate2(file, password, storeFlags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a new certificate key and put it in the file at <paramref name="file"/>.
|
||||
/// </summary>
|
||||
/// <param name="file">The path of the output file</param>
|
||||
/// <param name="password">The password of the new certificate</param>
|
||||
/// <returns>The generated certificate</returns>
|
||||
private static X509Certificate2 _GenerateCertificate(string file, string password)
|
||||
{
|
||||
SecureRandom random = new();
|
||||
|
||||
X509V3CertificateGenerator certificateGenerator = new();
|
||||
certificateGenerator.SetSerialNumber(BigIntegers.CreateRandomInRange(BigInteger.One,
|
||||
BigInteger.ValueOf(long.MaxValue), random));
|
||||
certificateGenerator.SetIssuerDN(new X509Name($"C=NL, O=SDG, CN=Kyoo"));
|
||||
certificateGenerator.SetSubjectDN(new X509Name($"C=NL, O=SDG, CN=Kyoo"));
|
||||
certificateGenerator.SetNotBefore(DateTime.UtcNow.Date);
|
||||
certificateGenerator.SetNotAfter(DateTime.UtcNow.Date.AddMonths(3));
|
||||
|
||||
KeyGenerationParameters keyGenerationParameters = new(random, 2048);
|
||||
RsaKeyPairGenerator keyPairGenerator = new();
|
||||
keyPairGenerator.Init(keyGenerationParameters);
|
||||
|
||||
AsymmetricCipherKeyPair subjectKeyPair = keyPairGenerator.GenerateKeyPair();
|
||||
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
|
||||
|
||||
const string signatureAlgorithm = "MD5WithRSA";
|
||||
Asn1SignatureFactory signatureFactory = new(signatureAlgorithm, subjectKeyPair.Private);
|
||||
X509Certificate bouncyCert = certificateGenerator.Generate(signatureFactory);
|
||||
|
||||
Pkcs12Store store = new Pkcs12StoreBuilder().Build();
|
||||
store.SetKeyEntry("Kyoo_key", new AsymmetricKeyEntry(subjectKeyPair.Private), new[]
|
||||
{
|
||||
new X509CertificateEntry(bouncyCert)
|
||||
});
|
||||
|
||||
using MemoryStream pfxStream = new();
|
||||
store.Save(pfxStream, password.ToCharArray(), random);
|
||||
X509Certificate2 certificate = new(pfxStream.ToArray(), password, X509KeyStorageFlags.Exportable);
|
||||
using FileStream fileStream = File.OpenWrite(file);
|
||||
pfxStream.WriteTo(fileStream);
|
||||
return certificate;
|
||||
}
|
||||
}
|
||||
}
|
@ -19,9 +19,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using IdentityModel;
|
||||
using IdentityServer4;
|
||||
using Kyoo.Abstractions.Models;
|
||||
|
||||
namespace Kyoo.Authentication
|
||||
{
|
||||
@ -30,35 +27,6 @@ namespace Kyoo.Authentication
|
||||
/// </summary>
|
||||
public static class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get claims of an user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user concerned</param>
|
||||
/// <returns>The list of claims the user has</returns>
|
||||
public static ICollection<Claim> GetClaims(this User user)
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new Claim(JwtClaimTypes.Subject, user.ID.ToString()),
|
||||
new Claim(JwtClaimTypes.Name, user.Username),
|
||||
new Claim(JwtClaimTypes.Picture, $"api/account/picture/{user.Slug}")
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a user to an IdentityServerUser.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to convert.</param>
|
||||
/// <returns>The corresponding identity server user.</returns>
|
||||
public static IdentityServerUser ToIdentityUser(this User user)
|
||||
{
|
||||
return new IdentityServerUser(user.ID.ToString())
|
||||
{
|
||||
DisplayName = user.Username,
|
||||
AdditionalClaims = new[] { new Claim("permissions", string.Join(',', user.Permissions)) }
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the permissions of an user.
|
||||
/// </summary>
|
||||
|
@ -1,19 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
|
||||
<PropertyGroup>
|
||||
<LoginRoot>../Kyoo.WebLogin/</LoginRoot>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="IdentityServer4" Version="4.1.2" />
|
||||
<PackageReference Include="IdentityServer4.Storage" Version="4.1.2" />
|
||||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" />
|
||||
<PackageReference Include="Portable.BouncyCastle" Version="1.8.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.12" />
|
||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.2" />
|
||||
|
||||
<ProjectReference Include="../Kyoo.Abstractions/Kyoo.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="$(LoginRoot)**;" />
|
||||
<Content Include="$(LoginRoot)**" Visible="false">
|
||||
|
@ -1,121 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using IdentityServer4.Models;
|
||||
|
||||
namespace Kyoo.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// The hard coded context of the identity server.
|
||||
/// </summary>
|
||||
public static class IdentityContext
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of identity resources supported (email, profile and openid)
|
||||
/// </summary>
|
||||
/// <returns>The list of identity resources supported</returns>
|
||||
public static IEnumerable<IdentityResource> GetIdentityResources()
|
||||
{
|
||||
return new List<IdentityResource>
|
||||
{
|
||||
new IdentityResources.OpenId(),
|
||||
new IdentityResources.Email(),
|
||||
new IdentityResources.Profile()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the list of officially supported clients.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You can add custom clients in the settings.json file.
|
||||
/// </remarks>
|
||||
/// <returns>The list of officially supported clients.</returns>
|
||||
public static IEnumerable<Client> GetClients()
|
||||
{
|
||||
return new List<Client>
|
||||
{
|
||||
new()
|
||||
{
|
||||
ClientId = "kyoo.webapp",
|
||||
|
||||
AccessTokenType = AccessTokenType.Jwt,
|
||||
AllowedGrantTypes = GrantTypes.Code,
|
||||
RequirePkce = true,
|
||||
RequireClientSecret = false,
|
||||
|
||||
AllowAccessTokensViaBrowser = true,
|
||||
AllowOfflineAccess = true,
|
||||
RequireConsent = false,
|
||||
|
||||
AllowedScopes = { "openid", "profile", "kyoo.read", "kyoo.write", "kyoo.play", "kyoo.admin" },
|
||||
RedirectUris = { "/", "/silent.html" },
|
||||
PostLogoutRedirectUris = { "/logout" }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The list of scopes supported by the API.
|
||||
/// </summary>
|
||||
/// <returns>The list of scopes</returns>
|
||||
public static IEnumerable<ApiScope> GetScopes()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new ApiScope
|
||||
{
|
||||
Name = "kyoo.read",
|
||||
DisplayName = "Read only access to the API.",
|
||||
},
|
||||
new ApiScope
|
||||
{
|
||||
Name = "kyoo.write",
|
||||
DisplayName = "Read and write access to the public API"
|
||||
},
|
||||
new ApiScope
|
||||
{
|
||||
Name = "kyoo.play",
|
||||
DisplayName = "Allow playback of movies and episodes."
|
||||
},
|
||||
new ApiScope
|
||||
{
|
||||
Name = "kyoo.admin",
|
||||
DisplayName = "Full access to the admin's API and the public API."
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The list of APIs (this is used to create Audiences)
|
||||
/// </summary>
|
||||
/// <returns>The list of apis</returns>
|
||||
public static IEnumerable<ApiResource> GetApis()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new ApiResource("kyoo", "Kyoo")
|
||||
{
|
||||
Scopes = GetScopes().Select(x => x.Name).ToArray()
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -29,9 +29,9 @@ namespace Kyoo.Authentication.Models
|
||||
public const string Path = "authentication";
|
||||
|
||||
/// <summary>
|
||||
/// The options for certificates
|
||||
/// The secret used to encrypt the jwt.
|
||||
/// </summary>
|
||||
public CertificateOption Certificate { get; set; }
|
||||
public string Secret { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Options for permissions
|
||||
|
@ -1,46 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace Kyoo.Authentication.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A typed option model for the certificate
|
||||
/// </summary>
|
||||
public class CertificateOption
|
||||
{
|
||||
/// <summary>
|
||||
/// The path to get this option from the root configuration.
|
||||
/// </summary>
|
||||
public const string Path = "authentication:certificate";
|
||||
|
||||
/// <summary>
|
||||
/// The path of the certificate file.
|
||||
/// </summary>
|
||||
public string File { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path of the old certificate file.
|
||||
/// </summary>
|
||||
public string OldFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The password of the certificates.
|
||||
/// </summary>
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
@ -60,17 +60,11 @@
|
||||
},
|
||||
|
||||
"authentication": {
|
||||
"certificate": {
|
||||
"file": "certificate.pfx",
|
||||
"oldFile": "oldCertificate.pfx",
|
||||
"password": "passphrase"
|
||||
},
|
||||
"permissions": {
|
||||
"default": ["overall.read", "overall.write", "overall.create", "overall.delete", "admin.read", "admin.write"],
|
||||
"newUser": ["overall.read", "overall.write", "overall.create", "overall.delete", "admin.read", "admin.write"]
|
||||
},
|
||||
"profilePicturePath": "users/",
|
||||
"clients": []
|
||||
},
|
||||
|
||||
"tvdb": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user