diff --git a/.github/workflows/build-dotnet.yml b/.github/workflows/build-dotnet.yml index ba99431..1395b38 100644 --- a/.github/workflows/build-dotnet.yml +++ b/.github/workflows/build-dotnet.yml @@ -15,7 +15,7 @@ jobs: - name: "Setup .NET Core" uses: actions/setup-dotnet@v1 with: - dotnet-version: "8.0.x" + dotnet-version: "5.0.x" - name: "Install dependencies" run: dotnet restore diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6696a94..c6ee551 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -25,7 +25,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 8.0.x + dotnet-version: 5.0.x - name: Initialize CodeQL uses: github/codeql-action/init@v1 diff --git a/.gitignore b/.gitignore index c43d422..03c9b93 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ bin/ obj/ -.idea/ -*.DotSettings.user .vs/ diff --git a/Jellyfin.Plugin.Keycloak/Configuration/PluginConfiguration.cs b/Jellyfin.Plugin.Keycloak/Configuration/PluginConfiguration.cs index bc018d6..12a92e3 100644 --- a/Jellyfin.Plugin.Keycloak/Configuration/PluginConfiguration.cs +++ b/Jellyfin.Plugin.Keycloak/Configuration/PluginConfiguration.cs @@ -1,94 +1,25 @@ using MediaBrowser.Model.Plugins; -namespace Jellyfin.Plugin.Keycloak.Configuration; - -/// -/// The main plugin. -/// -public class PluginConfiguration : BasePluginConfiguration +namespace Jellyfin.Plugin.Keycloak.Configuration { - /// - /// Initializes a new instance of the class. - /// - public PluginConfiguration() + + public class PluginConfiguration : BasePluginConfiguration { - // set default options here - this.Enabled = false; - this.CreateUser = true; - this.Enable2Fa = false; - this.AuthServerUrl = string.Empty; - this.Realm = "master"; - this.ClientId = string.Empty; - this.ClientSecret = string.Empty; - this.OAuthScope = string.Empty; - this.RolesTokenAttribute = string.Empty; - this.UsernameTokenAttribute = "preferred_username"; - this.EnableAllFolders = false; - this.EnabledFolders = System.Array.Empty(); + public bool CreateUser { get; set; } + public string AuthServerUrl { get; set; } + public string Realm { get; set; } + public string Resource { get; set; } + public string ClientSecret { get; set; } + + + public PluginConfiguration() + { + // set default options here + CreateUser = true; + AuthServerUrl = ""; + Realm = ""; + Resource = ""; + ClientSecret = ""; + } } - - /// - /// Gets or sets a value indicating whether Keycloak authentication is enabled. - /// - public bool Enabled { get; set; } - - /// - /// Gets or sets a value indicating whether creation of new users that dont exist in Jellyfin is enabled. - /// - public bool CreateUser { get; set; } - - /// - /// Gets or sets a value indicating whether 2-factor authentication (Password+TOTP). - /// - public bool Enable2Fa { get; set; } - - /// - /// Gets or sets Keycloak server URL. - /// - public string AuthServerUrl { get; set; } - - /// - /// Gets or sets Keycloak server realm. - /// - public string Realm { get; set; } - - /// - /// Gets or sets Keycloak client ID. - /// - public string ClientId { get; set; } - - /// - /// Gets or sets Keycloak client secret. - /// - public string ClientSecret { get; set; } - - /// - /// Gets or sets Keycloak OAuth scope. - /// - public string OAuthScope { get; set; } - - /// - /// Gets or sets Keycloak username token attribute. - /// - public string UsernameTokenAttribute { get; set; } - - /// - /// Gets or sets Keycloak roles token attribute. - /// - public string RolesTokenAttribute { get; set; } - - /// - /// Gets or sets a value indicating whether users without a role are allowed to log in. - /// - public bool AllowUsersWithoutRole { get; set; } - - /// - /// Gets or sets a value indicating whether to enable access to all library folders. - /// - public bool EnableAllFolders { get; set; } - - /// - /// Gets or sets a list of folder Ids which are enabled for access by default. - /// - public string[] EnabledFolders { get; set; } } diff --git a/Jellyfin.Plugin.Keycloak/Configuration/configPage.html b/Jellyfin.Plugin.Keycloak/Configuration/configPage.html index c11d588..c23d4e7 100644 --- a/Jellyfin.Plugin.Keycloak/Configuration/configPage.html +++ b/Jellyfin.Plugin.Keycloak/Configuration/configPage.html @@ -8,75 +8,33 @@
-

Keycloak Authentication

-
- -
-
- -
You need to add the TOTP (6 digits) to the password when logging in
-
- + +
Base Keycloak auth URI
- + +
Keycloak Realm
- - + + +
Keycloak Resource/Client
+
Client Secret
-
- - -
-
- - -
Access token attribute with the list of roles. Seperate keys with a '.' if the role list is part of a nested object.
-
-
- - -
Access token attribute with the username
-
-
- -
- - -
-
-
Enable access to certain libraries by default
-
Add library access to certain users by giving them the 'lib-<ID>' role
-

-
-
diff --git a/Jellyfin.Plugin.Keycloak/Jellyfin.Plugin.Keycloak.csproj b/Jellyfin.Plugin.Keycloak/Jellyfin.Plugin.Keycloak.csproj index 7204b0d..bf05667 100644 --- a/Jellyfin.Plugin.Keycloak/Jellyfin.Plugin.Keycloak.csproj +++ b/Jellyfin.Plugin.Keycloak/Jellyfin.Plugin.Keycloak.csproj @@ -1,15 +1,9 @@ - net8.0 - 2.0.0.1 - 2.0.0.1 - true - true - enable - AllEnabledByDefault - ../jellyfin.ruleset - CA1819 + net5.0 + 1.0.0.0 + 1.0.0.0 @@ -22,13 +16,6 @@ - - - - - - - diff --git a/Jellyfin.Plugin.Keycloak/KeyCloakAuthenticationProviderPlugin.cs b/Jellyfin.Plugin.Keycloak/KeyCloakAuthenticationProviderPlugin.cs index 8e2cc7a..0f74286 100644 --- a/Jellyfin.Plugin.Keycloak/KeyCloakAuthenticationProviderPlugin.cs +++ b/Jellyfin.Plugin.Keycloak/KeyCloakAuthenticationProviderPlugin.cs @@ -1,14 +1,10 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; using System.Net.Http; using System.Text.Json; -using System.Text.RegularExpressions; using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; -using Jellyfin.Plugin.Keycloak.Configuration; using JWT.Builder; using MediaBrowser.Common; using MediaBrowser.Common.Net; @@ -20,24 +16,150 @@ using Newtonsoft.Json.Linq; namespace Jellyfin.Plugin.Keycloak { - /// - /// KeyCloak Authentication Provider Plugin. - /// public class KeyCloakAuthenticationProviderPlugin : IAuthenticationProvider { private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger _logger; private readonly IApplicationHost _applicationHost; - private string _twoFactorPattern = @"(.*)(\d{6})$"; + private IUserManager _userManager; - /// - /// Initializes a new instance of the class. - /// - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - public KeyCloakAuthenticationProviderPlugin( - IHttpClientFactory httpClientFactory, + private bool CreateUser => Plugin.Instance.Configuration.CreateUser; + private String AuthServerUrl => Plugin.Instance.Configuration.AuthServerUrl; + private String Realm => Plugin.Instance.Configuration.Realm; + private String Resource => Plugin.Instance.Configuration.Resource; + private String ClientSecret => Plugin.Instance.Configuration.ClientSecret; + + private HttpClient GetHttpClient() + { + return _httpClientFactory.CreateClient(NamedClient.Default); + } + + private String TokenURI => $"{AuthServerUrl}/realms/{Realm}/protocol/openid-connect/token"; + + private async Task GetKeycloakUser(string username, string password) + { + var httpClient = GetHttpClient(); + var keyValues = new List>(); + keyValues.Add( new KeyValuePair("username", username)); + keyValues.Add( new KeyValuePair("password", password)); + keyValues.Add( new KeyValuePair("grant_type", "password")); + keyValues.Add( new KeyValuePair("client_id", Resource)); + if (!String.IsNullOrWhiteSpace(ClientSecret)) + { + keyValues.Add(new KeyValuePair("client_secret", ClientSecret)); + } + + var content = new FormUrlEncodedContent(keyValues); + var response = await httpClient.PostAsync(TokenURI, content).ConfigureAwait(false); + var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + var parsed = await JsonSerializer.DeserializeAsync(responseStream).ConfigureAwait(false); + if (parsed == null) + return null; + try + { + var jwtToken = JwtBuilder.Create().Decode>(parsed.access_token); + List perms = new List(); + try + { + var resourceAccess = (JObject)jwtToken["resource_access"]; + perms = ((JArray)(((JObject)resourceAccess[Resource])["roles"])).ToObject>(); + } + catch (Exception ex) + { + _logger.LogError(ex, $"Could not parse permissions for resource {Resource}"); + } + + return new KeycloakUser {Username = username, Permissions = perms}; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error parsing jwt token"); + } + + return null; + } + + private async Task UpdateUserInfo(KeycloakUser keycloakUser, User jellyfinUser) + { + jellyfinUser.SetPermission(PermissionKind.IsDisabled, true); + jellyfinUser.SetPermission(PermissionKind.IsAdministrator, false); + jellyfinUser.SetPermission(PermissionKind.EnableContentDownloading, false); + foreach (string permission in keycloakUser.Permissions) + { + switch (permission) + { + case "administrator": + jellyfinUser.SetPermission(PermissionKind.IsAdministrator, true); + break; + case "allowed_access": + jellyfinUser.SetPermission(PermissionKind.IsDisabled, false); + break; + } + } + await _userManager.UpdateUserAsync(jellyfinUser).ConfigureAwait(false); + return jellyfinUser; + } + + public async Task Authenticate(string username, string password) + { + _userManager ??= _applicationHost.Resolve(); + User user = null; + try + { + user = _userManager.GetUserByName(username); + } + catch (Exception e) + { + _logger.LogWarning("User Manager could not find a user for Keycloak User, this may not be fatal", e); + } + + KeycloakUser keycloakUser = await GetKeycloakUser(username, password); + if (keycloakUser == null) + { + throw new AuthenticationException("Error completing Keycloak login. Invalid username or password."); + } + + if (user == null) + { + if (CreateUser) + { + _logger.LogInformation($"Creating user {username}"); + user = await _userManager.CreateUserAsync(username).ConfigureAwait(false); + user.AuthenticationProviderId = GetType().FullName; + await UpdateUserInfo(keycloakUser, user); + } + else + { + _logger.LogError("Keycloak User not configured for Jellyfin: {username}", username); + throw new AuthenticationException( + $"Automatic User Creation is disabled and there is no Jellyfin user for authorized Uid: {username}"); + } + + } + else + { + await UpdateUserInfo(keycloakUser, user); + } + if (user.HasPermission(PermissionKind.IsDisabled)) + { + // If the user no longer has permission to access revoke all sessions for this user + _logger.LogInformation($"{username} is disabled, revoking all sessions"); + var sessionHandler = _applicationHost.Resolve(); + sessionHandler.RevokeUserTokens(user.Id, null); + } + return new ProviderAuthenticationResult { Username = username}; + } + + public bool HasPassword(User user) + { + return true; + } + + public Task ChangePassword(User user, string newPassword) + { + throw new System.NotImplementedException(); + } + public KeyCloakAuthenticationProviderPlugin(IHttpClientFactory httpClientFactory, IApplicationHost applicationHost, ILogger logger) { @@ -46,242 +168,7 @@ namespace Jellyfin.Plugin.Keycloak _applicationHost = applicationHost; } - private string TokenUri => $"{Cfg().AuthServerUrl}/realms/{Cfg().Realm}/protocol/openid-connect/token"; - - /// public string Name => "Keycloak-Authentication"; - - /// public bool IsEnabled => true; - - private HttpClient GetHttpClient() - { - return _httpClientFactory.CreateClient(NamedClient.Default); - } - - private PluginConfiguration Cfg() - { - return Plugin.Instance.Configuration; - } - - private async Task<(KeycloakUser?, bool)> GetKeycloakUser(string username, string password, string? totp) - { - var httpClient = GetHttpClient(); - var keyValues = new List> - { - new KeyValuePair("username", username), - new KeyValuePair("password", password), - new KeyValuePair("grant_type", "password"), - new KeyValuePair("client_id", Cfg().ClientId) - }; - if (!string.IsNullOrWhiteSpace(Cfg().ClientSecret)) - { - keyValues.Add(new KeyValuePair("client_secret", Cfg().ClientSecret)); - } - - if (!string.IsNullOrWhiteSpace(Cfg().OAuthScope)) - { - keyValues.Add(new KeyValuePair("scope", Cfg().OAuthScope)); - } - - if (!string.IsNullOrWhiteSpace(totp)) - { - keyValues.Add(new KeyValuePair("totp", totp)); - } - - var content = new FormUrlEncodedContent(keyValues); - var response = await httpClient.PostAsync(TokenUri, content).ConfigureAwait(false); - var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - - if (!response.IsSuccessStatusCode) - { - KeycloakErrorResponse? err = await JsonSerializer.DeserializeAsync(responseStream).ConfigureAwait(false); - if (err == null) - { - return (null, false); - } - else if (err.Error == "invalid_grant") - { - return (null, true); - } - else - { - _logger.LogError("Keycloak error {0}: {1}", err.Error, err.ErrorDescription); - return (null, err.Error == "invalid_grant"); - } - } - - KeycloakTokenResponse? parsed = await JsonSerializer.DeserializeAsync(responseStream).ConfigureAwait(false); - if (parsed == null) - { - _logger.LogError("Error parsing Keycloak token response"); - return (null, false); - } - - try - { - var jwtToken = JwtBuilder.Create().Decode>(parsed.AccessToken); - - Collection perms = new Collection(); - var permsObj = GetJwtAttribute(jwtToken, Cfg().RolesTokenAttribute); - if (permsObj != null) - { - perms = ((JArray)permsObj).ToObject>(); - } - - var usernameObj = GetJwtAttribute(jwtToken, Cfg().UsernameTokenAttribute); - if (usernameObj != null) - { - username = (string)usernameObj; - } - - KeycloakUser user = new KeycloakUser(username, perms); - - return (user, false); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error parsing jwt token"); - } - - return (null, false); - } - - private async Task UpdateUserInfo(KeycloakUser keycloakUser, User? jellyfinUser) - { - var userManager = _applicationHost.Resolve(); - if (jellyfinUser != null) - { - jellyfinUser.SetPermission(PermissionKind.IsDisabled, !keycloakUser.IsEnabled); - jellyfinUser.SetPermission(PermissionKind.IsAdministrator, keycloakUser.IsAdmin); - jellyfinUser.SetPermission(PermissionKind.EnableCollectionManagement, keycloakUser.IsEditor); - jellyfinUser.SetPermission(PermissionKind.EnableSubtitleManagement, keycloakUser.IsEditor); - jellyfinUser.SetPermission(PermissionKind.EnableAllFolders, Cfg().EnableAllFolders || keycloakUser.IsAdmin); - - var enabledFolders = new HashSet(Cfg().EnabledFolders); - enabledFolders.UnionWith(keycloakUser.EnabledFolders); - jellyfinUser.SetPreference(PreferenceKind.EnabledFolders, enabledFolders.ToArray()); - - await userManager.UpdateUserAsync(jellyfinUser).ConfigureAwait(false); - } - } - - private object? GetJwtAttribute(IDictionary jwt, string tokenAttribute) - { - if (!string.IsNullOrWhiteSpace(tokenAttribute)) - { - var parts = tokenAttribute.Split('.'); - try - { - IDictionary subObj = jwt; - for (int i = 0; i < parts.Length - 1; i++) - { - subObj = (IDictionary)subObj[parts[i]]; - } - - return subObj[parts[^1]]; - } - catch (Exception ex) - { - _logger.LogError(ex, "Could not extract {TokenAttribute} from JWT", tokenAttribute); - } - } - - return null; - } - - /// - public async Task Authenticate(string username, string password) - { - _logger.LogInformation("Keycloak login: {Username}", username); - var userManager = _applicationHost.Resolve(); - string password_t = password; - string? totp = null; - if (Cfg().Enable2Fa) - { - var match = Regex.Match(password, _twoFactorPattern); - if (match.Success) - { - password_t = match.Groups[1].Value; - totp = match.Groups[2].Value; - } - } - - (KeycloakUser? keycloakUser, bool retryNoOtp) = await GetKeycloakUser(username, password_t, totp).ConfigureAwait(false); - if (keycloakUser == null && totp != null && retryNoOtp) - { - (keycloakUser, _) = await GetKeycloakUser(username, password, null).ConfigureAwait(false); - } - - if (keycloakUser == null) - { - throw new AuthenticationException("Error completing Keycloak login. Invalid username or password."); - } - - keycloakUser.IsEnabled |= Cfg().AllowUsersWithoutRole; - - User? user = null; - try - { - user = userManager.GetUserByName(keycloakUser.Username); - } - catch (Exception e) - { - _logger.LogWarning("User Manager could not find a user for Keycloak User, this may not be fatal: {E}", e); - } - - if (user == null) - { - if (!keycloakUser.IsEnabled) - { - _logger.LogError("Keycloak User not allowed to use Jellyfin: {Username}", username); - throw new AuthenticationException("User is not allowed to use Jellyfin"); - } - else if (Cfg().CreateUser) - { - _logger.LogInformation("Creating user: {Username}", username); - user = await userManager.CreateUserAsync(username).ConfigureAwait(false); - var userAuthenticationProviderId = GetType().FullName; - if (userAuthenticationProviderId != null) - { - user.AuthenticationProviderId = userAuthenticationProviderId; - } - - await UpdateUserInfo(keycloakUser, user).ConfigureAwait(false); - } - else - { - _logger.LogError("Keycloak User not configured for Jellyfin: {Username}", username); - throw new AuthenticationException( - $"Automatic User Creation is disabled and there is no Jellyfin user for authorized Uid: {username}"); - } - } - else - { - await UpdateUserInfo(keycloakUser, user).ConfigureAwait(false); - } - - if (user != null && user.HasPermission(PermissionKind.IsDisabled)) - { - // If the user no longer has permission to access revoke all sessions for this user - _logger.LogInformation("{Username} is disabled, revoking all sessions", username); - var sessionHandler = _applicationHost.Resolve(); - await sessionHandler.RevokeUserTokens(user.Id, null).ConfigureAwait(false); - } - - return new ProviderAuthenticationResult { Username = username }; - } - - /// - public bool HasPassword(User user) - { - return true; - } - - /// - public Task ChangePassword(User user, string newPassword) - { - throw new NotImplementedException(); - } } } diff --git a/Jellyfin.Plugin.Keycloak/KeycloakAccessToken.cs b/Jellyfin.Plugin.Keycloak/KeycloakAccessToken.cs new file mode 100644 index 0000000..dcc8b45 --- /dev/null +++ b/Jellyfin.Plugin.Keycloak/KeycloakAccessToken.cs @@ -0,0 +1,6 @@ +using System.Collections.Generic; + +namespace Jellyfin.Plugin.Keycloak +{ + +} diff --git a/Jellyfin.Plugin.Keycloak/KeycloakErrorResponse.cs b/Jellyfin.Plugin.Keycloak/KeycloakErrorResponse.cs deleted file mode 100644 index b810ff1..0000000 --- a/Jellyfin.Plugin.Keycloak/KeycloakErrorResponse.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Runtime.Serialization; -using System.Text.Json.Serialization; - -namespace Jellyfin.Plugin.Keycloak -{ - /// - /// Response model for Keycloak errors. - /// - [DataContract] - public class KeycloakErrorResponse - { - /// - /// Gets or sets error code. - /// - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// - /// Gets or sets error description. - /// - [JsonPropertyName("error_description")] - public string? ErrorDescription { get; set; } - } -} diff --git a/Jellyfin.Plugin.Keycloak/KeycloakTokenResponse.cs b/Jellyfin.Plugin.Keycloak/KeycloakTokenResponse.cs index 63fcd82..89e586b 100644 --- a/Jellyfin.Plugin.Keycloak/KeycloakTokenResponse.cs +++ b/Jellyfin.Plugin.Keycloak/KeycloakTokenResponse.cs @@ -1,54 +1,17 @@ using System.Runtime.Serialization; -using System.Text.Json.Serialization; namespace Jellyfin.Plugin.Keycloak { - /// - /// Response model for the keycloak token. - /// [DataContract] public class KeycloakTokenResponse { - /// - /// Gets or sets the access token instance. - /// - [JsonPropertyName("access_token")] - public string? AccessToken { get; set; } - - /// - /// Gets or sets the expiry time instance. - /// - [JsonPropertyName("expires_in")] - public int ExpiresIn { get; set; } - - /// - /// Gets or sets the not-before-policy instance. - /// - [JsonPropertyName("not-before-policy")] - public int NotBeforePolicy { get; set; } - - /// - /// Gets or sets the refresh token instance. - /// - [JsonPropertyName("refresh_token")] - public string? RefreshToken { get; set; } - - /// - /// Gets or sets the scope instance. - /// - [JsonPropertyName("scope")] - public string? Scope { get; set; } - - /// - /// Gets or sets the session state instance. - /// - [JsonPropertyName("session_state")] - public string? SessionState { get; set; } - - /// - /// Gets or sets the token type instance. - /// - [JsonPropertyName("token_type")] - public string? TokenType { get; set; } + public string access_token { get; set; } + public int expires_in { get; set; } + [DataMember(Name = "not-before-policy")] + public int not_before_policy { get; set; } + public string refresh_token { get; set; } + public string scope { get; set; } + public string session_state { get; set; } + public string token_type { get; set; } } } diff --git a/Jellyfin.Plugin.Keycloak/KeycloakUser.cs b/Jellyfin.Plugin.Keycloak/KeycloakUser.cs index 0c7b578..65bf82d 100644 --- a/Jellyfin.Plugin.Keycloak/KeycloakUser.cs +++ b/Jellyfin.Plugin.Keycloak/KeycloakUser.cs @@ -1,72 +1,10 @@ using System.Collections.Generic; -using System.Collections.ObjectModel; namespace Jellyfin.Plugin.Keycloak { - /// - /// User Model for Keycloak Users. - /// public class KeycloakUser { - /// - /// Initializes a new instance of the class. - /// - /// Instance of the username of the user. - /// User roles. - public KeycloakUser(string username, Collection roles) - { - Username = username; - Roles = roles; - - IsAdmin = false; - IsEditor = false; - IsEnabled = false; - var enabledFolders = new List(); - - foreach (string permission in Roles) - { - IsAdmin |= permission == "admin"; - IsEditor |= permission == "editor"; - IsEnabled |= permission == "user"; - - if (permission.StartsWith("lib-", System.StringComparison.Ordinal)) - { - enabledFolders.Add(permission[4..]); - } - } - - IsEnabled |= IsAdmin || IsEditor; - EnabledFolders = enabledFolders.ToArray(); - } - - /// - /// Gets the value of the username of an user. - /// - public string Username { get; } - - /// - /// Gets the value of roles of an user. - /// - public Collection Roles { get; } - - /// - /// Gets a list of the enabled media libraries of an user. - /// - public string[] EnabledFolders { get; } - - /// - /// Gets a value indicating whether the user is an administrator. - /// - public bool IsAdmin { get; } - - /// - /// Gets a value indicating whether the user is an editor. - /// - public bool IsEditor { get; } - - /// - /// Gets or sets a value indicating whether the user should be enabled. - /// - public bool IsEnabled { get; set; } + public string Username { get; set; } + public List Permissions { get; set; } } } diff --git a/Jellyfin.Plugin.Keycloak/Plugin.cs b/Jellyfin.Plugin.Keycloak/Plugin.cs index cf529d0..6fb1811 100644 --- a/Jellyfin.Plugin.Keycloak/Plugin.cs +++ b/Jellyfin.Plugin.Keycloak/Plugin.cs @@ -8,33 +8,19 @@ using MediaBrowser.Model.Serialization; namespace Jellyfin.Plugin.Keycloak { - /// - /// Keycloak Plugin. - /// public class Plugin : BasePlugin, IHasWebPages { - /// - /// Initializes a new instance of the class. - /// - /// Instance of the interface. - /// Instance of the interface. + public override string Name => "Keycloak-Auth"; + + public override Guid Id => Guid.Parse("40886866-b3dd-4d6a-bf9b-25c83e6c3d10"); + public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) : base(applicationPaths, xmlSerializer) { Instance = this; } - /// - /// Gets the plugin instance. - /// - public static Plugin Instance { get; private set; } = null!; + public static Plugin Instance { get; private set; } - /// - public override string Name => "Keycloak-Auth"; - - /// - public override Guid Id => Guid.Parse("40886866-b3dd-4d6a-bf9b-25c83e6c3d10"); - - /// public IEnumerable GetPages() { return new[] @@ -42,7 +28,7 @@ namespace Jellyfin.Plugin.Keycloak new PluginPageInfo { Name = this.Name, - EmbeddedResourcePath = $"{GetType().Namespace}.Configuration.configPage.html" + EmbeddedResourcePath = string.Format("{0}.Configuration.configPage.html", GetType().Namespace) } }; } diff --git a/Jellyfin.Plugin.Keycloak/ServiceRegistrator.cs b/Jellyfin.Plugin.Keycloak/ServiceRegistrator.cs deleted file mode 100644 index 50afcff..0000000 --- a/Jellyfin.Plugin.Keycloak/ServiceRegistrator.cs +++ /dev/null @@ -1,18 +0,0 @@ -using MediaBrowser.Controller; -using MediaBrowser.Controller.Authentication; -using MediaBrowser.Controller.Plugins; -using Microsoft.Extensions.DependencyInjection; - -namespace Jellyfin.Plugin.Keycloak; - -/// -/// Register LDAP services. -/// -public class ServiceRegistrator : IPluginServiceRegistrator -{ - /// - public void RegisterServices(IServiceCollection serviceCollection, IServerApplicationHost applicationHost) - { - serviceCollection.AddSingleton(); - } -} diff --git a/jellyfin.ruleset b/jellyfin.ruleset deleted file mode 100644 index e7bc7ec..0000000 --- a/jellyfin.ruleset +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -