mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-24 15:05:24 -07:00
Merge branch 'feature/ldap' of github.com:dpraul/Ombi into work/ldap-integration
This commit is contained in:
commit
49b4cf8286
25 changed files with 730 additions and 25 deletions
168
src/Ombi.Core/Authentication/LdapUserManager.cs
Normal file
168
src/Ombi.Core/Authentication/LdapUserManager.cs
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Novell.Directory.Ldap;
|
||||||
|
using Ombi.Core.Settings;
|
||||||
|
using Ombi.Settings.Settings.Models;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
|
||||||
|
namespace Ombi.Core.Authentication
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ldap Authentication Provider
|
||||||
|
/// </summary>
|
||||||
|
public class LdapUserManager : ILdapUserManager
|
||||||
|
{
|
||||||
|
public LdapUserManager(ILogger<LdapUserManager> logger, ISettingsService<LdapSettings> ldapSettings)
|
||||||
|
{
|
||||||
|
_ldapSettingsService = ldapSettings;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly ISettingsService<LdapSettings> _ldapSettingsService;
|
||||||
|
private readonly ILogger<LdapUserManager> _logger;
|
||||||
|
|
||||||
|
public async Task<LdapSettings> GetSettings()
|
||||||
|
{
|
||||||
|
return await _ldapSettingsService.GetSettingsAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<OmbiUser> LdapEntryToOmbiUser(LdapEntry entry)
|
||||||
|
{
|
||||||
|
var settings = await GetSettings();
|
||||||
|
var userName = GetLdapAttribute(entry, settings.UsernameAttribute).StringValue;
|
||||||
|
|
||||||
|
return new OmbiUser
|
||||||
|
{
|
||||||
|
UserType = UserType.LdapUser,
|
||||||
|
ProviderUserId = entry.Dn,
|
||||||
|
UserName = userName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private LdapAttribute GetLdapAttribute(LdapEntry userEntry, string attr)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return userEntry.GetAttribute(attr);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<LdapConnection> BindLdapConnection(string username, string password)
|
||||||
|
{
|
||||||
|
var settings = await GetSettings();
|
||||||
|
|
||||||
|
var ldapClient = new LdapConnection { SecureSocketLayer = settings.UseSsl };
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (settings.SkipSslVerify)
|
||||||
|
{
|
||||||
|
ldapClient.UserDefinedServerCertValidationDelegate += LdapClient_UserDefinedServerCertValidationDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
ldapClient.Connect(settings.Hostname, settings.Port);
|
||||||
|
if (settings.UseStartTls)
|
||||||
|
{
|
||||||
|
ldapClient.StartTls();
|
||||||
|
}
|
||||||
|
|
||||||
|
ldapClient.Bind(username, password);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.LogError(e, "Failed to Connect or Bind to server");
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ldapClient.UserDefinedServerCertValidationDelegate -= LdapClient_UserDefinedServerCertValidationDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ldapClient.Bound)
|
||||||
|
{
|
||||||
|
ldapClient.Dispose();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ldapClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ILdapSearchResults> SearchLdapUsers(LdapConnection ldapClient)
|
||||||
|
{
|
||||||
|
var settings = await GetSettings();
|
||||||
|
|
||||||
|
string[] searchAttributes = { settings.UsernameAttribute };
|
||||||
|
|
||||||
|
_logger.LogDebug("Search: {1} {2} @ {3}", settings.BaseDn, settings.SearchFilter, settings.Hostname);
|
||||||
|
return ldapClient.Search(settings.BaseDn, LdapConnection.ScopeSub, settings.SearchFilter, searchAttributes, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ILdapSearchResults> GetLdapUsers()
|
||||||
|
{
|
||||||
|
var settings = await GetSettings();
|
||||||
|
|
||||||
|
using var ldapClient = await BindLdapConnection(settings.BindUserDn, settings.BindUserPassword);
|
||||||
|
|
||||||
|
return await SearchLdapUsers(ldapClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LdapEntry> LocateLdapUser(string username)
|
||||||
|
{
|
||||||
|
var settings = await GetSettings();
|
||||||
|
|
||||||
|
using var ldapClient = await BindLdapConnection(settings.BindUserDn, settings.BindUserPassword);
|
||||||
|
var ldapUsers = await SearchLdapUsers(ldapClient);
|
||||||
|
if (ldapUsers == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (ldapUsers.HasMore())
|
||||||
|
{
|
||||||
|
var currentUser = ldapUsers.Next();
|
||||||
|
var foundUsername = GetLdapAttribute(currentUser, settings.UsernameAttribute)?.StringValue;
|
||||||
|
if (foundUsername == username)
|
||||||
|
{
|
||||||
|
return currentUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Authenticate user against the ldap server.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">Username to authenticate.</param>
|
||||||
|
/// <param name="password">Password to authenticate.</param>
|
||||||
|
public async Task<bool> Authenticate(OmbiUser user, string password)
|
||||||
|
{
|
||||||
|
var ldapUser = await LocateLdapUser(user.UserName);
|
||||||
|
|
||||||
|
if (ldapUser == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var ldapClient = await BindLdapConnection(ldapUser.Dn, password);
|
||||||
|
return (bool)ldapClient?.Bound;
|
||||||
|
} catch (Exception)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool LdapClient_UserDefinedServerCertValidationDelegate(
|
||||||
|
object sender,
|
||||||
|
System.Security.Cryptography.X509Certificates.X509Certificate certificate,
|
||||||
|
System.Security.Cryptography.X509Certificates.X509Chain chain,
|
||||||
|
System.Net.Security.SslPolicyErrors sslPolicyErrors)
|
||||||
|
=> true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,12 +26,14 @@
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using Novell.Directory.Ldap;
|
||||||
using Ombi.Api.Emby;
|
using Ombi.Api.Emby;
|
||||||
using Ombi.Api.Jellyfin;
|
using Ombi.Api.Jellyfin;
|
||||||
using Ombi.Api.Plex;
|
using Ombi.Api.Plex;
|
||||||
|
@ -50,9 +52,14 @@ namespace Ombi.Core.Authentication
|
||||||
IPasswordHasher<OmbiUser> passwordHasher, IEnumerable<IUserValidator<OmbiUser>> userValidators,
|
IPasswordHasher<OmbiUser> passwordHasher, IEnumerable<IUserValidator<OmbiUser>> userValidators,
|
||||||
IEnumerable<IPasswordValidator<OmbiUser>> passwordValidators, ILookupNormalizer keyNormalizer,
|
IEnumerable<IPasswordValidator<OmbiUser>> passwordValidators, ILookupNormalizer keyNormalizer,
|
||||||
IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<OmbiUser>> logger, IPlexApi plexApi,
|
IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<OmbiUser>> logger, IPlexApi plexApi,
|
||||||
|
<<<<<<< HEAD
|
||||||
IEmbyApiFactory embyApi, ISettingsService<EmbySettings> embySettings,
|
IEmbyApiFactory embyApi, ISettingsService<EmbySettings> embySettings,
|
||||||
IJellyfinApiFactory jellyfinApi, ISettingsService<JellyfinSettings> jellyfinSettings,
|
IJellyfinApiFactory jellyfinApi, ISettingsService<JellyfinSettings> jellyfinSettings,
|
||||||
ISettingsService<AuthenticationSettings> auth)
|
ISettingsService<AuthenticationSettings> auth)
|
||||||
|
=======
|
||||||
|
IEmbyApiFactory embyApi, ISettingsService<EmbySettings> embySettings, ISettingsService<AuthenticationSettings> auth,
|
||||||
|
ILdapUserManager ldapUserManager, ISettingsService<UserManagementSettings> userManagementSettings)
|
||||||
|
>>>>>>> 691c70804f203fab858b1079a1cf3d5e4adbf322
|
||||||
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
|
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
|
||||||
{
|
{
|
||||||
_plexApi = plexApi;
|
_plexApi = plexApi;
|
||||||
|
@ -61,14 +68,49 @@ namespace Ombi.Core.Authentication
|
||||||
_embySettings = embySettings;
|
_embySettings = embySettings;
|
||||||
_jellyfinSettings = jellyfinSettings;
|
_jellyfinSettings = jellyfinSettings;
|
||||||
_authSettings = auth;
|
_authSettings = auth;
|
||||||
|
_ldapUserManager = ldapUserManager;
|
||||||
|
_userManagementSettings = userManagementSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly IPlexApi _plexApi;
|
private readonly IPlexApi _plexApi;
|
||||||
private readonly IEmbyApiFactory _embyApi;
|
private readonly IEmbyApiFactory _embyApi;
|
||||||
|
<<<<<<< HEAD
|
||||||
private readonly IJellyfinApiFactory _jellyfinApi;
|
private readonly IJellyfinApiFactory _jellyfinApi;
|
||||||
|
=======
|
||||||
|
private readonly ILdapUserManager _ldapUserManager;
|
||||||
|
>>>>>>> 691c70804f203fab858b1079a1cf3d5e4adbf322
|
||||||
private readonly ISettingsService<EmbySettings> _embySettings;
|
private readonly ISettingsService<EmbySettings> _embySettings;
|
||||||
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
|
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
|
||||||
private readonly ISettingsService<AuthenticationSettings> _authSettings;
|
private readonly ISettingsService<AuthenticationSettings> _authSettings;
|
||||||
|
private readonly ISettingsService<UserManagementSettings> _userManagementSettings;
|
||||||
|
|
||||||
|
public async Task<OmbiUser> FindUser(string userName)
|
||||||
|
{
|
||||||
|
var user = await FindByNameAsync(userName);
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
user = await FindByEmailAsync(userName);
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
user.EmailLogin = true;
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ldapSettings = await _ldapUserManager.GetSettings();
|
||||||
|
if (!(ldapSettings.IsEnabled && ldapSettings.CreateUsersAtLogin))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ldapUser = await _ldapUserManager.LocateLdapUser(userName);
|
||||||
|
if (ldapUser == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return await CreateOmbiUserFromLdapEntry(ldapUser);
|
||||||
|
}
|
||||||
|
|
||||||
public override async Task<bool> CheckPasswordAsync(OmbiUser user, string password)
|
public override async Task<bool> CheckPasswordAsync(OmbiUser user, string password)
|
||||||
{
|
{
|
||||||
|
@ -90,9 +132,15 @@ namespace Ombi.Core.Authentication
|
||||||
{
|
{
|
||||||
return await CheckEmbyPasswordAsync(user, password);
|
return await CheckEmbyPasswordAsync(user, password);
|
||||||
}
|
}
|
||||||
|
<<<<<<< HEAD
|
||||||
if (user.UserType == UserType.JellyfinUser)
|
if (user.UserType == UserType.JellyfinUser)
|
||||||
{
|
{
|
||||||
return await CheckJellyfinPasswordAsync(user, password);
|
return await CheckJellyfinPasswordAsync(user, password);
|
||||||
|
=======
|
||||||
|
if (user.UserType == UserType.LdapUser)
|
||||||
|
{
|
||||||
|
return await CheckLdapPasswordAsync(user, password);
|
||||||
|
>>>>>>> 691c70804f203fab858b1079a1cf3d5e4adbf322
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -227,5 +275,41 @@ namespace Ombi.Core.Authentication
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
private async Task<bool> CheckLdapPasswordAsync(OmbiUser user, string password)
|
||||||
|
{
|
||||||
|
var ldapSettings = await _ldapUserManager.GetSettings();
|
||||||
|
if (!ldapSettings.IsEnabled)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await _ldapUserManager.Authenticate(user, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<OmbiUser> CreateOmbiUserFromLdapEntry(LdapEntry entry)
|
||||||
|
{
|
||||||
|
var newUser = await _ldapUserManager.LdapEntryToOmbiUser(entry);
|
||||||
|
var userManagementSettings = await _userManagementSettings.GetSettingsAsync();
|
||||||
|
|
||||||
|
var result = await CreateAsync(newUser);
|
||||||
|
if (!result.Succeeded)
|
||||||
|
{
|
||||||
|
foreach (var identityError in result.Errors)
|
||||||
|
{
|
||||||
|
Logger.LogError(LoggingEvents.Authentication, identityError.Description);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userManagementSettings.DefaultRoles.Any())
|
||||||
|
{
|
||||||
|
foreach (var defaultRole in userManagementSettings.DefaultRoles)
|
||||||
|
{
|
||||||
|
await AddToRoleAsync(newUser, defaultRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newUser;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
20
src/Ombi.Core/ILdapUserManager.cs
Normal file
20
src/Ombi.Core/ILdapUserManager.cs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Novell.Directory.Ldap;
|
||||||
|
using Ombi.Settings.Settings.Models;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
|
||||||
|
namespace Ombi.Core.Authentication
|
||||||
|
{
|
||||||
|
public interface ILdapUserManager
|
||||||
|
{
|
||||||
|
Task<LdapSettings> GetSettings();
|
||||||
|
|
||||||
|
Task<bool> Authenticate(OmbiUser user, string password);
|
||||||
|
|
||||||
|
Task<ILdapSearchResults> GetLdapUsers();
|
||||||
|
|
||||||
|
Task<LdapEntry> LocateLdapUser(string username);
|
||||||
|
|
||||||
|
Task<OmbiUser> LdapEntryToOmbiUser(LdapEntry entry);
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,7 @@
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.0" />
|
||||||
<PackageReference Include="MusicBrainzAPI" Version="2.0.1" />
|
<PackageReference Include="MusicBrainzAPI" Version="2.0.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
|
<PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="3.3.1" />
|
||||||
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />
|
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Core" Version="1.1.0" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Core" Version="1.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -52,6 +52,7 @@ using Ombi.Schedule.Jobs.Jellyfin;
|
||||||
using Ombi.Schedule.Jobs.Ombi;
|
using Ombi.Schedule.Jobs.Ombi;
|
||||||
using Ombi.Schedule.Jobs.Plex;
|
using Ombi.Schedule.Jobs.Plex;
|
||||||
using Ombi.Schedule.Jobs.Sonarr;
|
using Ombi.Schedule.Jobs.Sonarr;
|
||||||
|
using Ombi.Schedule.Jobs.Ldap;
|
||||||
using Ombi.Store.Repository.Requests;
|
using Ombi.Store.Repository.Requests;
|
||||||
using Ombi.Updater;
|
using Ombi.Updater;
|
||||||
using Ombi.Api.Telegram;
|
using Ombi.Api.Telegram;
|
||||||
|
@ -101,6 +102,7 @@ namespace Ombi.DependencyInjection
|
||||||
services.AddTransient<IMusicSender, MusicSender>();
|
services.AddTransient<IMusicSender, MusicSender>();
|
||||||
services.AddTransient<IMassEmailSender, MassEmailSender>();
|
services.AddTransient<IMassEmailSender, MassEmailSender>();
|
||||||
services.AddTransient<IPlexOAuthManager, PlexOAuthManager>();
|
services.AddTransient<IPlexOAuthManager, PlexOAuthManager>();
|
||||||
|
services.AddTransient<ILdapUserManager, LdapUserManager>();
|
||||||
services.AddTransient<IVoteEngine, VoteEngine>();
|
services.AddTransient<IVoteEngine, VoteEngine>();
|
||||||
services.AddTransient<IDemoMovieSearchEngine, DemoMovieSearchEngine>();
|
services.AddTransient<IDemoMovieSearchEngine, DemoMovieSearchEngine>();
|
||||||
services.AddTransient<IDemoTvSearchEngine, DemoTvSearchEngine>();
|
services.AddTransient<IDemoTvSearchEngine, DemoTvSearchEngine>();
|
||||||
|
@ -231,6 +233,7 @@ namespace Ombi.DependencyInjection
|
||||||
services.AddTransient<IPlexUserImporter, PlexUserImporter>();
|
services.AddTransient<IPlexUserImporter, PlexUserImporter>();
|
||||||
services.AddTransient<IEmbyUserImporter, EmbyUserImporter>();
|
services.AddTransient<IEmbyUserImporter, EmbyUserImporter>();
|
||||||
services.AddTransient<IJellyfinUserImporter, JellyfinUserImporter>();
|
services.AddTransient<IJellyfinUserImporter, JellyfinUserImporter>();
|
||||||
|
services.AddTransient<ILdapUserImporter, LdapUserImporter>();
|
||||||
services.AddTransient<IWelcomeEmail, WelcomeEmail>();
|
services.AddTransient<IWelcomeEmail, WelcomeEmail>();
|
||||||
services.AddTransient<ICouchPotatoSync, CouchPotatoSync>();
|
services.AddTransient<ICouchPotatoSync, CouchPotatoSync>();
|
||||||
services.AddTransient<IProcessProvider, ProcessProvider>();
|
services.AddTransient<IProcessProvider, ProcessProvider>();
|
||||||
|
|
7
src/Ombi.Schedule/Jobs/Ldap/ILdapUserImporter.cs
Normal file
7
src/Ombi.Schedule/Jobs/Ldap/ILdapUserImporter.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
namespace Ombi.Schedule.Jobs.Ldap
|
||||||
|
{
|
||||||
|
public interface ILdapUserImporter : IBaseJob
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
87
src/Ombi.Schedule/Jobs/Ldap/LdapUserImporter.cs
Normal file
87
src/Ombi.Schedule/Jobs/Ldap/LdapUserImporter.cs
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Ombi.Core.Authentication;
|
||||||
|
using Ombi.Core.Settings;
|
||||||
|
using Ombi.Hubs;
|
||||||
|
using Ombi.Settings.Settings.Models;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
using Quartz;
|
||||||
|
|
||||||
|
namespace Ombi.Schedule.Jobs.Ldap
|
||||||
|
{
|
||||||
|
public class LdapUserImporter : ILdapUserImporter
|
||||||
|
{
|
||||||
|
public LdapUserImporter(OmbiUserManager userManager, ILdapUserManager ldapUserManager,
|
||||||
|
ISettingsService<LdapSettings> ldapSettings, ISettingsService<UserManagementSettings> ums, IHubContext<NotificationHub> notification)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
_ldapUserManager = ldapUserManager;
|
||||||
|
_ldapSettings = ldapSettings;
|
||||||
|
_userManagementSettings = ums;
|
||||||
|
_notification = notification;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly OmbiUserManager _userManager;
|
||||||
|
private readonly ILdapUserManager _ldapUserManager;
|
||||||
|
private readonly ISettingsService<LdapSettings> _ldapSettings;
|
||||||
|
private readonly ISettingsService<UserManagementSettings> _userManagementSettings;
|
||||||
|
private readonly IHubContext<NotificationHub> _notification;
|
||||||
|
|
||||||
|
public async Task Execute(IJobExecutionContext job)
|
||||||
|
{
|
||||||
|
var userManagementSettings = await _userManagementSettings.GetSettingsAsync();
|
||||||
|
if (!userManagementSettings.ImportLdapUsers)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var settings = await _ldapSettings.GetSettingsAsync();
|
||||||
|
if (!settings.IsEnabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||||
|
.SendAsync(NotificationHub.NotificationEvent, "LDAP User Importer Started");
|
||||||
|
var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.LdapUser).ToListAsync();
|
||||||
|
|
||||||
|
var allLdapUsers = await _ldapUserManager.GetLdapUsers();
|
||||||
|
|
||||||
|
while (allLdapUsers.HasMore())
|
||||||
|
{
|
||||||
|
var currentUser = allLdapUsers.Next();
|
||||||
|
var existingEmbyUser = allUsers.FirstOrDefault(x => x.ProviderUserId == currentUser.Dn);
|
||||||
|
if (existingEmbyUser != null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _userManager.CreateOmbiUserFromLdapEntry(currentUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||||
|
.SendAsync(NotificationHub.NotificationEvent, "LDAP User Importer Finished");
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _disposed;
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_userManager?.Dispose();
|
||||||
|
}
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ using Ombi.Schedule.Jobs;
|
||||||
using Ombi.Schedule.Jobs.Couchpotato;
|
using Ombi.Schedule.Jobs.Couchpotato;
|
||||||
using Ombi.Schedule.Jobs.Emby;
|
using Ombi.Schedule.Jobs.Emby;
|
||||||
using Ombi.Schedule.Jobs.Jellyfin;
|
using Ombi.Schedule.Jobs.Jellyfin;
|
||||||
|
using Ombi.Schedule.Jobs.Ldap;
|
||||||
using Ombi.Schedule.Jobs.Lidarr;
|
using Ombi.Schedule.Jobs.Lidarr;
|
||||||
using Ombi.Schedule.Jobs.Ombi;
|
using Ombi.Schedule.Jobs.Ombi;
|
||||||
using Ombi.Schedule.Jobs.Plex;
|
using Ombi.Schedule.Jobs.Plex;
|
||||||
|
@ -56,6 +57,7 @@ namespace Ombi.Schedule
|
||||||
await AddDvrApps(s);
|
await AddDvrApps(s);
|
||||||
await AddSystem(s);
|
await AddSystem(s);
|
||||||
await AddNotifications(s);
|
await AddNotifications(s);
|
||||||
|
await AddLdap(s);
|
||||||
|
|
||||||
// Run Quartz
|
// Run Quartz
|
||||||
await OmbiQuartz.Start();
|
await OmbiQuartz.Start();
|
||||||
|
@ -113,5 +115,9 @@ namespace Ombi.Schedule
|
||||||
{
|
{
|
||||||
await OmbiQuartz.Instance.AddJob<INotificationService>(nameof(INotificationService), "Notifications", null);
|
await OmbiQuartz.Instance.AddJob<INotificationService>(nameof(INotificationService), "Notifications", null);
|
||||||
}
|
}
|
||||||
|
private static async Task AddLdap(JobSettings s)
|
||||||
|
{
|
||||||
|
await OmbiQuartz.Instance.AddJob<ILdapUserImporter>(nameof(ILdapUserImporter), "LDAP", JobSettingsHelper.UserImporter(s));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
81
src/Ombi.Settings/Settings/Models/LdapSettings.cs
Normal file
81
src/Ombi.Settings/Settings/Models/LdapSettings.cs
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
namespace Ombi.Settings.Settings.Models
|
||||||
|
{
|
||||||
|
public class LdapSettings : Settings
|
||||||
|
{
|
||||||
|
public LdapSettings()
|
||||||
|
{
|
||||||
|
IsEnabled = false;
|
||||||
|
CreateUsersAtLogin = true;
|
||||||
|
Hostname = "ldap-server.example.tld";
|
||||||
|
BaseDn = "o=domains,dc=example,dc=tld";
|
||||||
|
Port = 389;
|
||||||
|
UsernameAttribute = "uid";
|
||||||
|
SearchFilter = "(memberOf=cn=Users,dc=example,dc=tld)";
|
||||||
|
BindUserDn = "cn=BindUser,dc=example,dc=tld";
|
||||||
|
BindUserPassword = "password";
|
||||||
|
UseSsl = true;
|
||||||
|
UseStartTls = false;
|
||||||
|
SkipSslVerify = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether LDAP is enabled.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsEnabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether users should be automatically created at login.
|
||||||
|
/// </summary>
|
||||||
|
public bool CreateUsersAtLogin { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the ldap server ip or url.
|
||||||
|
/// </summary>
|
||||||
|
public string Hostname { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the ldap base search dn.
|
||||||
|
/// </summary>
|
||||||
|
public string BaseDn { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the ldap port.
|
||||||
|
/// </summary>
|
||||||
|
public int Port { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the ldap username attribute.
|
||||||
|
/// </summary>
|
||||||
|
public string UsernameAttribute { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the ldap user search filter.
|
||||||
|
/// </summary>
|
||||||
|
public string SearchFilter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the ldap bind user dn.
|
||||||
|
/// </summary>
|
||||||
|
public string BindUserDn { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the ldap bind user password.
|
||||||
|
/// </summary>
|
||||||
|
public string BindUserPassword { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to use ssl when connecting to the ldap server.
|
||||||
|
/// </summary>
|
||||||
|
public bool UseSsl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to use StartTls when connecting to the ldap server.
|
||||||
|
/// </summary>
|
||||||
|
public bool UseStartTls { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to skip ssl verification.
|
||||||
|
/// </summary>
|
||||||
|
public bool SkipSslVerify { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ namespace Ombi.Settings.Settings.Models
|
||||||
public bool ImportPlexUsers { get; set; }
|
public bool ImportPlexUsers { get; set; }
|
||||||
public bool ImportEmbyUsers { get; set; }
|
public bool ImportEmbyUsers { get; set; }
|
||||||
public bool ImportJellyfinUsers { get; set; }
|
public bool ImportJellyfinUsers { get; set; }
|
||||||
|
public bool ImportLdapUsers { get; set; }
|
||||||
public int MovieRequestLimit { get; set; }
|
public int MovieRequestLimit { get; set; }
|
||||||
public int EpisodeRequestLimit { get; set; }
|
public int EpisodeRequestLimit { get; set; }
|
||||||
public string DefaultStreamingCountry { get; set; } = "US";
|
public string DefaultStreamingCountry { get; set; } = "US";
|
||||||
|
|
|
@ -35,5 +35,6 @@ namespace Ombi.Store.Entities
|
||||||
EmbyUser = 3,
|
EmbyUser = 3,
|
||||||
EmbyConnectUser = 4,
|
EmbyConnectUser = 4,
|
||||||
JellyfinUser = 5,
|
JellyfinUser = 5,
|
||||||
|
LdapUser = 6,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,6 +194,21 @@ export interface IAuthenticationSettings extends ISettings {
|
||||||
enableOAuth: boolean;
|
enableOAuth: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ILdapSettings extends ISettings {
|
||||||
|
isEnabled: boolean;
|
||||||
|
hostname: string;
|
||||||
|
port: number;
|
||||||
|
baseDn: string;
|
||||||
|
useSsl: boolean;
|
||||||
|
useStartTls: boolean;
|
||||||
|
skipSslVerify: boolean;
|
||||||
|
bindUserDn: string;
|
||||||
|
bindUserPassword: string;
|
||||||
|
usernameAttribute: string;
|
||||||
|
searchFilter: string;
|
||||||
|
createUsersAtLogin: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ICustomPage extends ISettings {
|
export interface ICustomPage extends ISettings {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
fontAwesomeIcon: string;
|
fontAwesomeIcon: string;
|
||||||
|
@ -206,6 +221,7 @@ export interface IUserManagementSettings extends ISettings {
|
||||||
importPlexAdmin: boolean;
|
importPlexAdmin: boolean;
|
||||||
importEmbyUsers: boolean;
|
importEmbyUsers: boolean;
|
||||||
importJellyfinUsers: boolean;
|
importJellyfinUsers: boolean;
|
||||||
|
importLdapUsers: boolean;
|
||||||
defaultRoles: string[];
|
defaultRoles: string[];
|
||||||
movieRequestLimit: number;
|
movieRequestLimit: number;
|
||||||
episodeRequestLimit: number;
|
episodeRequestLimit: number;
|
||||||
|
|
|
@ -35,6 +35,10 @@ export class JobService extends ServiceHelpers {
|
||||||
return this.http.post<boolean>(`${this.url}jellyfinUserImporter/`, {headers: this.headers});
|
return this.http.post<boolean>(`${this.url}jellyfinUserImporter/`, {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public runLdapImporter(): Observable<boolean> {
|
||||||
|
return this.http.post<boolean>(`${this.url}ldapUserImporter/`, {headers: this.headers});
|
||||||
|
}
|
||||||
|
|
||||||
public runPlexCacher(): Observable<boolean> {
|
public runPlexCacher(): Observable<boolean> {
|
||||||
return this.http.post<boolean>(`${this.url}plexcontentcacher/`, {headers: this.headers});
|
return this.http.post<boolean>(`${this.url}plexcontentcacher/`, {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import {
|
||||||
IVoteSettings,
|
IVoteSettings,
|
||||||
ITwilioSettings,
|
ITwilioSettings,
|
||||||
IWebhookNotificationSettings,
|
IWebhookNotificationSettings,
|
||||||
|
ILdapSettings,
|
||||||
} from "../interfaces";
|
} from "../interfaces";
|
||||||
|
|
||||||
import { ServiceHelpers } from "./service.helpers";
|
import { ServiceHelpers } from "./service.helpers";
|
||||||
|
@ -133,6 +134,14 @@ export class SettingsService extends ServiceHelpers {
|
||||||
return this.http.post<boolean>(`${this.url}/Authentication`, JSON.stringify(settings), {headers: this.headers});
|
return this.http.post<boolean>(`${this.url}/Authentication`, JSON.stringify(settings), {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getLdap(): Observable<ILdapSettings> {
|
||||||
|
return this.http.get<ILdapSettings>(`${this.url}/ldap`, {headers: this.headers});
|
||||||
|
}
|
||||||
|
|
||||||
|
public saveLdap(settings: ILdapSettings): Observable<boolean> {
|
||||||
|
return this.http.post<boolean>(`${this.url}/ldap`, JSON.stringify(settings), {headers: this.headers});
|
||||||
|
}
|
||||||
|
|
||||||
// Using http since we need it not to be authenticated to get the landing page settings
|
// Using http since we need it not to be authenticated to get the landing page settings
|
||||||
public getLandingPage(): Observable<ILandingPageSettings> {
|
public getLandingPage(): Observable<ILandingPageSettings> {
|
||||||
return this.http.get<ILandingPageSettings>(`${this.url}/LandingPage`, {headers: this.headers});
|
return this.http.get<ILandingPageSettings>(`${this.url}/LandingPage`, {headers: this.headers});
|
||||||
|
|
78
src/Ombi/ClientApp/src/app/settings/ldap/ldap.component.html
Normal file
78
src/Ombi/ClientApp/src/app/settings/ldap/ldap.component.html
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
<settings-menu></settings-menu>
|
||||||
|
<fieldset *ngIf="form" class="small-middle-container">
|
||||||
|
<legend>LDAP Settings</legend>
|
||||||
|
<form
|
||||||
|
[formGroup]="form"
|
||||||
|
(ngSubmit)="onSubmit(form)"
|
||||||
|
class="md-form-field"
|
||||||
|
>
|
||||||
|
<div class="form-group">
|
||||||
|
<mat-checkbox formControlName="isEnabled">
|
||||||
|
LDAP Enabled
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<mat-checkbox formControlName="createUsersAtLogin">
|
||||||
|
Create Users at Login
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Hostname</mat-label>
|
||||||
|
<input matInput required formControlName="hostname">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Port</mat-label>
|
||||||
|
<input matInput type="number" required formControlName="port">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Base DN</mat-label>
|
||||||
|
<input matInput required formControlName="baseDn">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-checkbox formControlName="useSsl">
|
||||||
|
Use SSL
|
||||||
|
</mat-checkbox>
|
||||||
|
<mat-checkbox formControlName="useStartTls">
|
||||||
|
Use StartTLS
|
||||||
|
</mat-checkbox>
|
||||||
|
<mat-checkbox formControlName="skipSslVerify">
|
||||||
|
Skip SSL Verification
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Bind User DN</mat-label>
|
||||||
|
<input matInput required formControlName="bindUserDn">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Bind User Password</mat-label>
|
||||||
|
<input matInput required type="password" formControlName="bindUserPassword">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Username Attribute</mat-label>
|
||||||
|
<input matInput required formControlName="usernameAttribute">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Search Filter</mat-label>
|
||||||
|
<input matInput required formControlName="searchFilter">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<button
|
||||||
|
mat-raised-button
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
[disabled]="form.invalid"
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</fieldset>
|
|
@ -0,0 +1,9 @@
|
||||||
|
.small-middle-container {
|
||||||
|
margin: auto;
|
||||||
|
width: 95%;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-checkbox {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
62
src/Ombi/ClientApp/src/app/settings/ldap/ldap.component.ts
Normal file
62
src/Ombi/ClientApp/src/app/settings/ldap/ldap.component.ts
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||||
|
import { FormBuilder, FormGroup } from "@angular/forms";
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
|
import { NotificationService } from "../../services/notification.service";
|
||||||
|
import { SettingsService } from "../../services/settings.service";
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: "./ldap.component.html",
|
||||||
|
styleUrls: ["./ldap.component.scss"],
|
||||||
|
})
|
||||||
|
export class LdapComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
public form: FormGroup;
|
||||||
|
|
||||||
|
private sub: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private settingsService: SettingsService,
|
||||||
|
private notificationService: NotificationService,
|
||||||
|
private formBuilder: FormBuilder
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public ngOnInit() {
|
||||||
|
this.sub = this.settingsService.getLdap().subscribe(ldapSettings => {
|
||||||
|
this.form = this.formBuilder.group({
|
||||||
|
isEnabled: [ldapSettings.isEnabled],
|
||||||
|
hostname: [ldapSettings.hostname],
|
||||||
|
port: [ldapSettings.port],
|
||||||
|
baseDn: [ldapSettings.baseDn],
|
||||||
|
useSsl: [ldapSettings.useSsl],
|
||||||
|
useStartTls: [ldapSettings.useStartTls],
|
||||||
|
skipSslVerify: [ldapSettings.skipSslVerify],
|
||||||
|
bindUserDn: [ldapSettings.bindUserDn],
|
||||||
|
bindUserPassword: [ldapSettings.bindUserPassword],
|
||||||
|
usernameAttribute: [ldapSettings.usernameAttribute],
|
||||||
|
searchFilter: [ldapSettings.searchFilter],
|
||||||
|
createUsersAtLogin: [ldapSettings.createUsersAtLogin],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnDestroy() {
|
||||||
|
this.sub.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
public onSubmit(form: FormGroup) {
|
||||||
|
if (form.invalid) {
|
||||||
|
this.notificationService.error("Please check your entered values");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.settingsService.saveLdap(form.value).subscribe(x => {
|
||||||
|
if (x) {
|
||||||
|
this.notificationService.success("Successfully saved LDAP settings");
|
||||||
|
} else {
|
||||||
|
this.notificationService.success("There was an error when saving LDAP settings");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,6 +48,7 @@ import { UpdateComponent } from "./update/update.component";
|
||||||
import { UserManagementComponent } from "./usermanagement/usermanagement.component";
|
import { UserManagementComponent } from "./usermanagement/usermanagement.component";
|
||||||
import { VoteComponent } from "./vote/vote.component";
|
import { VoteComponent } from "./vote/vote.component";
|
||||||
import { WikiComponent } from "./wiki.component";
|
import { WikiComponent } from "./wiki.component";
|
||||||
|
import { LdapComponent } from "./ldap/ldap.component";
|
||||||
|
|
||||||
import { SettingsMenuComponent } from "./settingsmenu.component";
|
import { SettingsMenuComponent } from "./settingsmenu.component";
|
||||||
|
|
||||||
|
@ -97,6 +98,7 @@ const routes: Routes = [
|
||||||
{ path: "SickRage", component: SickRageComponent, canActivate: [AuthGuard] },
|
{ path: "SickRage", component: SickRageComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "Issues", component: IssuesComponent, canActivate: [AuthGuard] },
|
{ path: "Issues", component: IssuesComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "Authentication", component: AuthenticationComponent, canActivate: [AuthGuard] },
|
{ path: "Authentication", component: AuthenticationComponent, canActivate: [AuthGuard] },
|
||||||
|
{ path: "Ldap", component: LdapComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "Mobile", component: MobileComponent, canActivate: [AuthGuard] },
|
{ path: "Mobile", component: MobileComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "MassEmail", component: MassEmailComponent, canActivate: [AuthGuard] },
|
{ path: "MassEmail", component: MassEmailComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "Newsletter", component: NewsletterComponent, canActivate: [AuthGuard] },
|
{ path: "Newsletter", component: NewsletterComponent, canActivate: [AuthGuard] },
|
||||||
|
@ -158,6 +160,7 @@ const routes: Routes = [
|
||||||
TelegramComponent,
|
TelegramComponent,
|
||||||
IssuesComponent,
|
IssuesComponent,
|
||||||
AuthenticationComponent,
|
AuthenticationComponent,
|
||||||
|
LdapComponent,
|
||||||
MobileComponent,
|
MobileComponent,
|
||||||
MassEmailComponent,
|
MassEmailComponent,
|
||||||
NewsletterComponent,
|
NewsletterComponent,
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<button mat-menu-item [routerLink]="['/Settings/Issues']"><i class="fas fa-exclamation-triangle icon-spacing"></i> Issues</button>
|
<button mat-menu-item [routerLink]="['/Settings/Issues']"><i class="fas fa-exclamation-triangle icon-spacing"></i> Issues</button>
|
||||||
<button mat-menu-item [routerLink]="['/Settings/UserManagement']"><i class="fas fa-users-cog icon-spacing"></i> User Management</button>
|
<button mat-menu-item [routerLink]="['/Settings/UserManagement']"><i class="fas fa-users-cog icon-spacing"></i> User Management</button>
|
||||||
<button mat-menu-item [routerLink]="['/Settings/Authentication']"><i class="fas fa-sign-in-alt icon-spacing"></i> Authentication</button>
|
<button mat-menu-item [routerLink]="['/Settings/Authentication']"><i class="fas fa-sign-in-alt icon-spacing"></i> Authentication</button>
|
||||||
|
<button mat-menu-item [routerLink]="['/Settings/Ldap']"><i class="fas fa-address-card icon-spacing"></i> Ldap</button>
|
||||||
<!-- <button mat-menu-item [routerLink]="['/Settings/Vote']">Vote</button> -->
|
<!-- <button mat-menu-item [routerLink]="['/Settings/Vote']">Vote</button> -->
|
||||||
<button mat-menu-item [routerLink]="['/Settings/TheMovieDb']"><i class="fas fa-film icon-spacing"></i> The Movie Database</button>
|
<button mat-menu-item [routerLink]="['/Settings/TheMovieDb']"><i class="fas fa-film icon-spacing"></i> The Movie Database</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
|
|
|
@ -45,7 +45,12 @@
|
||||||
|
|
||||||
<p-autoComplete [(ngModel)]="bannedJellyfinUsers" [suggestions]="filteredJellyfinUsers" [multiple]="true" field="username" (completeMethod)="filterJellyfinList($event)"></p-autoComplete>
|
<p-autoComplete [(ngModel)]="bannedJellyfinUsers" [suggestions]="filteredJellyfinUsers" [multiple]="true" field="username" (completeMethod)="filterJellyfinList($event)"></p-autoComplete>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="ldapEnabled">
|
||||||
|
<div class="form-group">
|
||||||
|
<mat-checkbox id="importLdapUsers" [(ngModel)]="settings.importLdapUsers">Import LDAP Users</mat-checkbox>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
|
|
|
@ -12,7 +12,11 @@ export class UserManagementComponent implements OnInit {
|
||||||
|
|
||||||
public plexEnabled: boolean;
|
public plexEnabled: boolean;
|
||||||
public embyEnabled: boolean;
|
public embyEnabled: boolean;
|
||||||
|
<<<<<<< HEAD
|
||||||
public jellyfinEnabled: boolean;
|
public jellyfinEnabled: boolean;
|
||||||
|
=======
|
||||||
|
public ldapEnabled: boolean;
|
||||||
|
>>>>>>> 691c70804f203fab858b1079a1cf3d5e4adbf322
|
||||||
public settings: IUserManagementSettings;
|
public settings: IUserManagementSettings;
|
||||||
public claims: ICheckbox[];
|
public claims: ICheckbox[];
|
||||||
|
|
||||||
|
@ -45,7 +49,11 @@ export class UserManagementComponent implements OnInit {
|
||||||
this.settingsService.getUserManagementSettings().subscribe(x => {
|
this.settingsService.getUserManagementSettings().subscribe(x => {
|
||||||
this.settings = x;
|
this.settings = x;
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
if(x.importEmbyUsers || x.importJellyfinUsers || x.importPlexUsers) {
|
if(x.importEmbyUsers || x.importJellyfinUsers || x.importPlexUsers) {
|
||||||
|
=======
|
||||||
|
if(x.importEmbyUsers || x.importPlexUsers || x.importLdapUsers) {
|
||||||
|
>>>>>>> 691c70804f203fab858b1079a1cf3d5e4adbf322
|
||||||
this.enableImportButton = true;
|
this.enableImportButton = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +108,11 @@ export class UserManagementComponent implements OnInit {
|
||||||
});
|
});
|
||||||
this.settingsService.getPlex().subscribe(x => this.plexEnabled = x.enable);
|
this.settingsService.getPlex().subscribe(x => this.plexEnabled = x.enable);
|
||||||
this.settingsService.getEmby().subscribe(x => this.embyEnabled = x.enable);
|
this.settingsService.getEmby().subscribe(x => this.embyEnabled = x.enable);
|
||||||
|
<<<<<<< HEAD
|
||||||
this.settingsService.getJellyfin().subscribe(x => this.jellyfinEnabled = x.enable);
|
this.settingsService.getJellyfin().subscribe(x => this.jellyfinEnabled = x.enable);
|
||||||
|
=======
|
||||||
|
this.settingsService.getLdap().subscribe(x => this.ldapEnabled = x.isEnabled);
|
||||||
|
>>>>>>> 691c70804f203fab858b1079a1cf3d5e4adbf322
|
||||||
}
|
}
|
||||||
|
|
||||||
public submit(): void {
|
public submit(): void {
|
||||||
|
@ -110,9 +122,14 @@ export class UserManagementComponent implements OnInit {
|
||||||
this.settings.defaultRoles = enabledClaims.map((claim) => claim.value);
|
this.settings.defaultRoles = enabledClaims.map((claim) => claim.value);
|
||||||
this.settings.bannedPlexUserIds = this.bannedPlexUsers.map((u) => u.id);
|
this.settings.bannedPlexUserIds = this.bannedPlexUsers.map((u) => u.id);
|
||||||
this.settings.bannedEmbyUserIds = this.bannedEmbyUsers.map((u) => u.id);
|
this.settings.bannedEmbyUserIds = this.bannedEmbyUsers.map((u) => u.id);
|
||||||
|
<<<<<<< HEAD
|
||||||
this.settings.bannedJellyfinUserIds = this.bannedJellyfinUsers.map((u) => u.id);
|
this.settings.bannedJellyfinUserIds = this.bannedJellyfinUsers.map((u) => u.id);
|
||||||
|
|
||||||
if(this.settings.importEmbyUsers || this.settings.importJellyfinUsers || this.settings.importPlexUsers) {
|
if(this.settings.importEmbyUsers || this.settings.importJellyfinUsers || this.settings.importPlexUsers) {
|
||||||
|
=======
|
||||||
|
|
||||||
|
if(this.settings.importEmbyUsers || this.settings.importPlexUsers) {
|
||||||
|
>>>>>>> 691c70804f203fab858b1079a1cf3d5e4adbf322
|
||||||
this.enableImportButton = true;
|
this.enableImportButton = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +158,11 @@ export class UserManagementComponent implements OnInit {
|
||||||
|
|
||||||
this.jobService.runPlexImporter().subscribe();
|
this.jobService.runPlexImporter().subscribe();
|
||||||
this.jobService.runEmbyImporter().subscribe();
|
this.jobService.runEmbyImporter().subscribe();
|
||||||
|
<<<<<<< HEAD
|
||||||
this.jobService.runJellyfinImporter().subscribe();
|
this.jobService.runJellyfinImporter().subscribe();
|
||||||
|
=======
|
||||||
|
this.jobService.runLdapImporter().subscribe();
|
||||||
|
>>>>>>> 691c70804f203fab858b1079a1cf3d5e4adbf322
|
||||||
}
|
}
|
||||||
|
|
||||||
private filter(query: string, users: IUsersModel[]): IUsersModel[] {
|
private filter(query: string, users: IUsersModel[]): IUsersModel[] {
|
||||||
|
|
|
@ -90,6 +90,7 @@
|
||||||
<span *ngIf="u.userType === 3">Emby User</span>
|
<span *ngIf="u.userType === 3">Emby User</span>
|
||||||
<span *ngIf="u.userType === 4">Emby Connect User</span>
|
<span *ngIf="u.userType === 4">Emby Connect User</span>
|
||||||
<span *ngIf="u.userType === 5">Jellyfin User</span>
|
<span *ngIf="u.userType === 5">Jellyfin User</span>
|
||||||
|
<span *ngIf="u.userType === 6">LDAP User</span> </td>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@ -136,6 +137,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
<mat-form-field appearance="outline" class="full">
|
<mat-form-field appearance="outline" class="full">
|
||||||
<mat-label>Movie Request Limit</mat-label>
|
<mat-label>Movie Request Limit</mat-label>
|
||||||
<input matInput id="movieRequestLimit" name="movieRequestLimit" [(ngModel)]="bulkMovieLimit">
|
<input matInput id="movieRequestLimit" name="movieRequestLimit" [(ngModel)]="bulkMovieLimit">
|
||||||
|
@ -159,6 +161,23 @@
|
||||||
|
|
||||||
|
|
||||||
<button type="button" mat-raised-button (click)="bulkUpdate()">Update Users</button>
|
<button type="button" mat-raised-button (click)="bulkUpdate()">Update Users</button>
|
||||||
|
=======
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="movieRequestLimit" class="control-label">Movie Request Limit</label>
|
||||||
|
<div>
|
||||||
|
<input type="text" [(ngModel)]="bulkMovieLimit" class="form-control form-small form-control-custom " id="movieRequestLimit" name="movieRequestLimit" value="{{bulkMovieLimit}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="episodeRequestLimit" class="control-label">Episode Request Limit</label>
|
||||||
|
<div>
|
||||||
|
<input type="text" [(ngModel)]="bulkEpisodeLimit" class="form-control form-small form-control-custom " id="episodeRequestLimit" name="episodeRequestLimit" value="{{bulkEpisodeLimit}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-success-outline" (click)="bulkUpdate()">Update Users</button>
|
||||||
|
>>>>>>> 691c70804f203fab858b1079a1cf3d5e4adbf322
|
||||||
</p-sidebar>
|
</p-sidebar>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -11,6 +11,7 @@ using Ombi.Schedule.Jobs.Ombi;
|
||||||
using Ombi.Schedule.Jobs.Plex;
|
using Ombi.Schedule.Jobs.Plex;
|
||||||
using Ombi.Schedule.Jobs.Plex.Interfaces;
|
using Ombi.Schedule.Jobs.Plex.Interfaces;
|
||||||
using Ombi.Schedule.Jobs.Radarr;
|
using Ombi.Schedule.Jobs.Radarr;
|
||||||
|
using Ombi.Schedule.Jobs.Ldap;
|
||||||
using Quartz;
|
using Quartz;
|
||||||
|
|
||||||
namespace Ombi.Controllers.V1
|
namespace Ombi.Controllers.V1
|
||||||
|
@ -115,6 +116,16 @@ namespace Ombi.Controllers.V1
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs the LDAP User importer
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost("ldapuserimporter")]
|
||||||
|
public async Task<bool> LdapUserImporter()
|
||||||
|
{
|
||||||
|
await OmbiQuartz.TriggerJob(nameof(ILdapUserImporter), "LDAP");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Runs the Plex Content Cacher
|
/// Runs the Plex Content Cacher
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -467,6 +467,28 @@ namespace Ombi.Controllers.V1
|
||||||
return await Get<AuthenticationSettings>();
|
return await Get<AuthenticationSettings>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save the LDAP settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="settings">The settings.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost("ldap")]
|
||||||
|
public async Task<bool> LdapSettings([FromBody] LdapSettings settings)
|
||||||
|
{
|
||||||
|
return await Save(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the LDAP Settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet("ldap")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<LdapSettings> LdapSettings()
|
||||||
|
{
|
||||||
|
return await Get<LdapSettings>();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Save the Radarr settings.
|
/// Save the Radarr settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -51,8 +51,7 @@ namespace Ombi.Controllers.V1
|
||||||
{
|
{
|
||||||
if (!model.UsePlexOAuth)
|
if (!model.UsePlexOAuth)
|
||||||
{
|
{
|
||||||
var user = await _userManager.FindByNameAsync(model.Username);
|
var user = await _userManager.FindUser(model.Username);
|
||||||
|
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
// Could this be an email login?
|
// Could this be an email login?
|
||||||
|
@ -67,7 +66,6 @@ namespace Ombi.Controllers.V1
|
||||||
user.EmailLogin = true;
|
user.EmailLogin = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Verify Password
|
// Verify Password
|
||||||
if (await _userManager.CheckPasswordAsync(user, model.Password))
|
if (await _userManager.CheckPasswordAsync(user, model.Password))
|
||||||
{
|
{
|
||||||
|
@ -187,17 +185,11 @@ namespace Ombi.Controllers.V1
|
||||||
var account = await _plexOAuthManager.GetAccount(accessToken);
|
var account = await _plexOAuthManager.GetAccount(accessToken);
|
||||||
|
|
||||||
// Get the ombi user
|
// Get the ombi user
|
||||||
var user = await _userManager.FindByNameAsync(account.user.username);
|
var user = await _userManager.FindUser(account.user.username);
|
||||||
|
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
// Could this be an email login?
|
return new UnauthorizedResult();
|
||||||
user = await _userManager.FindByEmailAsync(account.user.email);
|
|
||||||
|
|
||||||
if (user == null)
|
|
||||||
{
|
|
||||||
return new UnauthorizedResult();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return await CreateToken(true, user);
|
return await CreateToken(true, user);
|
||||||
|
@ -228,17 +220,11 @@ namespace Ombi.Controllers.V1
|
||||||
[HttpPost("requirePassword")]
|
[HttpPost("requirePassword")]
|
||||||
public async Task<bool> DoesUserRequireAPassword([FromBody] UserAuthModel model)
|
public async Task<bool> DoesUserRequireAPassword([FromBody] UserAuthModel model)
|
||||||
{
|
{
|
||||||
var user = await _userManager.FindByNameAsync(model.Username);
|
var user = await _userManager.FindUser(model.Username);
|
||||||
|
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
// Could this be an email login?
|
return true;
|
||||||
user = await _userManager.FindByEmailAsync(model.Username);
|
|
||||||
|
|
||||||
if (user == null)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var requires = await _userManager.RequiresPassword(user);
|
var requires = await _userManager.RequiresPassword(user);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue