diff --git a/Kyoo/Controllers/AuthManager.cs b/Kyoo/Controllers/AuthManager.cs new file mode 100644 index 00000000..a813ab66 --- /dev/null +++ b/Kyoo/Controllers/AuthManager.cs @@ -0,0 +1,114 @@ +using System; +using System.IO; +using System.Linq; +using System.Security.Claims; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; +using IdentityServer4.Extensions; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Configuration; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Operators; +using Org.BouncyCastle.Crypto.Prng; +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.Controllers +{ + public class AuthManager + { + public const string CertificateFile = "certificate.pfx"; + + public static X509Certificate2 GetSiginCredential(IConfiguration configuration) + { + if (File.Exists(CertificateFile)) + { + return new X509Certificate2(CertificateFile, configuration.GetValue("certificatePassword"), + X509KeyStorageFlags.MachineKeySet | + X509KeyStorageFlags.PersistKeySet | + X509KeyStorageFlags.Exportable + ); + } + + SecureRandom random = new SecureRandom(); + + X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator(); + certificateGenerator.SetSerialNumber(BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.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.AddYears(1)); + + KeyGenerationParameters keyGenerationParameters = new KeyGenerationParameters(random, 2048); + RsaKeyPairGenerator keyPairGenerator = new RsaKeyPairGenerator(); + keyPairGenerator.Init(keyGenerationParameters); + + AsymmetricCipherKeyPair subjectKeyPair = keyPairGenerator.GenerateKeyPair(); + certificateGenerator.SetPublicKey(subjectKeyPair.Public); + + AsymmetricCipherKeyPair issuerKeyPair = subjectKeyPair; + const string signatureAlgorithm = "SHA256WithRSA"; + Asn1SignatureFactory signatureFactory = new Asn1SignatureFactory(signatureAlgorithm,issuerKeyPair.Private); + X509Certificate bouncyCert = certificateGenerator.Generate(signatureFactory); + + X509Certificate2 certificate; + + Pkcs12Store store = new Pkcs12StoreBuilder().Build(); + store.SetKeyEntry("Kyoo_key", new AsymmetricKeyEntry(subjectKeyPair.Private), new [] {new X509CertificateEntry(bouncyCert)}); + string pass = configuration.GetValue("certificatePassword"); //Guid.NewGuid().ToString("x"); + + using (MemoryStream pfxStream = new MemoryStream()) + { + store.Save(pfxStream, pass.ToCharArray(), random); + certificate = new X509Certificate2(pfxStream.ToArray(), pass, X509KeyStorageFlags.Exportable); + using (FileStream fileStream = File.OpenWrite(CertificateFile)) + pfxStream.WriteTo(fileStream); + } + return certificate; + } + } + + public class AuthorizationValidatorHandler : AuthorizationHandler + { + private readonly IConfiguration _configuration; + + public AuthorizationValidatorHandler(IConfiguration configuration) + { + _configuration = configuration; + } + + protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AuthorizationValidator requirement) + { + if (!context.User.IsAuthenticated()) + { + string defaultPerms = _configuration.GetValue("defaultPermissions"); + if (defaultPerms.Split(',').Contains(requirement.Permission.ToLower())) + context.Succeed(requirement); + } + else + { + Claim perms = context.User.Claims.FirstOrDefault(x => x.Type == "permissions"); + if (perms != null && perms.Value.Split(",").Contains(requirement.Permission.ToLower())) + context.Succeed(requirement); + } + + return Task.CompletedTask; + } + } + + public class AuthorizationValidator : IAuthorizationRequirement + { + public string Permission; + + public AuthorizationValidator(string permission) + { + Permission = permission; + } + } +} \ No newline at end of file diff --git a/Kyoo/Controllers/AuthorizationValidator.cs b/Kyoo/Controllers/AuthorizationValidator.cs deleted file mode 100644 index 9f50cd87..00000000 --- a/Kyoo/Controllers/AuthorizationValidator.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -using IdentityServer4.Extensions; -using Microsoft.AspNetCore.Authorization; -using Microsoft.Extensions.Configuration; - -namespace Kyoo.Controllers -{ - public class AuthorizationValidatorHandler : AuthorizationHandler - { - private readonly IConfiguration _configuration; - - public AuthorizationValidatorHandler(IConfiguration configuration) - { - _configuration = configuration; - } - - protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AuthorizationValidator requirement) - { - if (!context.User.IsAuthenticated()) - { - string defaultPerms = _configuration.GetValue("defaultPermissions"); - if (defaultPerms.Split(',').Contains(requirement.Permission.ToLower())) - context.Succeed(requirement); - } - else - { - Claim perms = context.User.Claims.FirstOrDefault(x => x.Type == "permissions"); - if (perms != null && perms.Value.Split(",").Contains(requirement.Permission.ToLower())) - context.Succeed(requirement); - } - - return Task.CompletedTask; - } - } - - public class AuthorizationValidator : IAuthorizationRequirement - { - public string Permission; - - public AuthorizationValidator(string permission) - { - Permission = permission; - } - } -} \ No newline at end of file diff --git a/Kyoo/Kyoo.csproj b/Kyoo/Kyoo.csproj index 09edfffa..e2474f2e 100644 --- a/Kyoo/Kyoo.csproj +++ b/Kyoo/Kyoo.csproj @@ -18,6 +18,7 @@ + diff --git a/Kyoo/Startup.cs b/Kyoo/Startup.cs index 0ec5c504..182f3e91 100644 --- a/Kyoo/Startup.cs +++ b/Kyoo/Startup.cs @@ -61,7 +61,6 @@ namespace Kyoo options.UserInteraction.LogoutUrl = publicUrl + "logout"; }) .AddAspNetIdentity() - .AddSigningCredentials() .AddConfigurationStore(options => { options.ConfigureDbContext = builder => @@ -78,7 +77,7 @@ namespace Kyoo .AddInMemoryIdentityResources(IdentityContext.GetIdentityResources()) .AddInMemoryApiResources(IdentityContext.GetApis()) .AddProfileService() - .AddDeveloperSigningCredential(); // TODO remove the developer signin + .AddSigningCredential(AuthManager.GetSiginCredential(Configuration)); services.AddAuthentication() .AddJwtBearer(options => diff --git a/Kyoo/appsettings.json b/Kyoo/appsettings.json index 40785057..b0a48d86 100644 --- a/Kyoo/appsettings.json +++ b/Kyoo/appsettings.json @@ -10,22 +10,17 @@ } }, "AllowedHosts": "*", - "ConnectionStrings": { "Database": "Data Source=kyoo.db" }, - - "IdentityServer": { - "Key": { - "Type": "Development" - } - }, + + "certificatePassword": "passphrase", "transmuxTempPath": "cached/kyoo/transmux", "transcodeTempPath": "cached/kyoo/transcode", "peoplePath": "people", "profilePicturePath": "users/", "plugins": "plugins/", - "defaultPermissions": "read,play", + "defaultPermissions": "", "regex": "(\\/(?.*)\\/)?.*\\/(?.+?)(( S(?\\d+)E(?\\d+)| (?\\d+)))?\\.", } diff --git a/Kyoo/tempkey.rsa b/Kyoo/tempkey.rsa deleted file mode 100644 index 5fa49c77..00000000 --- a/Kyoo/tempkey.rsa +++ /dev/null @@ -1 +0,0 @@ -{"KeyId":"mkaygTF8pb-42wV_HvSUCQ","Parameters":{"D":"usyImLSKe8Gvh65XyygNoe9bCffxcB9maRAAL9tXou89QHc4WhPvCjRDlryOwNUxNWJvduDXJm+AenWbSx7/PNVzaKaK6j/GKt9OMsD//9ubEswP9zhNFn9zAzmWsp2wSMEM+1fU6VcXc9MCwjySP3DtHiw3ZwFUvfP4pm3PhKwaI3TKe2rmB9mwziiv9SSd+bwbKlVlGmMM4UVMwD/VYmJZZMB8NaQY2PmdJHztyp4NYJIMFnGFaJVN9GSFC1qv2btKhlZrL7InGdATbzUC82+Ff0st5YX4omJyb8DJ25SiCSXzKkeLWafcCedYdJwIPSxAgd9edCQLUNvORa26qQ==","DP":"0x2WZoveXkd9cYs4xGpL3cKmg5RQe1IBNy19tqLhpiCOqtzgz7agCbEBSsHsPQVclrQMp+GvBbuWTKEAj4DR/N2o+ir6V0W80VN8/2K4PX7wo9ryJAmUXNp4b231ubJv4neDGqF2j0g+WWxt5Gh6gx+FJjkGENYs5VJ7kFSc+fs=","DQ":"HQr0bM//+68PwegI6PSmFYgNMciMsnXGpn9xElU2Ed8EiF8dRW3LBjl8y3tsuZVwHbteZtlZJqUtd0kuwkC+5Kts1dEds8FOXfrZgpt/n+APOr14707yaxrcWhGkJu9kCjRsAbhsxtScxQE/sUb+5naTF5ypmORplL3O6ZnJ8L0=","Exponent":"AQAB","InverseQ":"IYckPpRj0eq7UF3YrzSt7sLPYdz/S1nfBJl4gNKEYvnwysaxg2iOq2u/4hu7+r1Jf8+j6e1dcOJqN4jPXyq2ycQ0X5BLNHENXBjvRrfaUD35bBMz2vKRq423sXPAV2k1Mpt6StZSAjyxF741IIQ+X1W5IVuuO9awK9ruw9bpR2I=","Modulus":"yoIp2j1kI5sku7k16xJNLEt7uw4LQY+UX3onsgdf69aNNXk2j6IyoW6dE9lAGRYKsNkKSH2kvUBTHCX78j0Mg0dInNyB1s7MgS5t8ypc4y5lIE+xFEXKAKeH6lZ9NC+PNwtCSY9iNAteiPO1ittmzkS7QR9fgHQ/8NwYuU3AxQUxf5QULuYWIE4rKryQHqlQzqvg7cZizcuINXxG3oUiSGWg1aYQUcjY07wWJFvVwsV7pjKTSL0edDCoqg8bQ/F97aDuP4e0QKaALs1GkXb854Kp977EitxLOAYkJrq4X/OkTZjwXjQhXnDwiwacuCnqLpDz4OX1Nr7EKJz1ZzmGrw==","P":"7zjThXlm/qG07URGqKTnq1QWGwoCCxXfVK5u0xbzhuPtjISAk2ijs4Bp7XNbQD63zdO52k7F8od8s4HXBjCG5rzpMNxpMef3SPfBX2f726XtQQYhXEW90iXeIUl55Hp41M+CO9GQs4XOy+k/AtkDbbaR7EKAQP7w4ddhRSJUyqM=","Q":"2LYnkzwnZdd4Femjg/6whVLFJZ8g78dew/0SnlX++3ShOX9GheACcunKVs6LD8X9ALay2ondx+4qRuep0wphc4UK6HoN9S/GnhJZDt2GjfAPPPegEfOEW+jIcX/COYX1unfcCVb17Cl+dWfQRa6RXtvfputA1u6N+wb0wtcmwIU="}} \ No newline at end of file