Merge branch 'feature/ldap' of github.com:dpraul/Ombi into work/ldap-integration

This commit is contained in:
Paannda 2021-03-29 22:15:33 +00:00
commit 49b4cf8286
25 changed files with 730 additions and 25 deletions

View 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;
}
}

View file

@ -26,12 +26,14 @@
#endregion
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Novell.Directory.Ldap;
using Ombi.Api.Emby;
using Ombi.Api.Jellyfin;
using Ombi.Api.Plex;
@ -50,9 +52,14 @@ namespace Ombi.Core.Authentication
IPasswordHasher<OmbiUser> passwordHasher, IEnumerable<IUserValidator<OmbiUser>> userValidators,
IEnumerable<IPasswordValidator<OmbiUser>> passwordValidators, ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<OmbiUser>> logger, IPlexApi plexApi,
<<<<<<< HEAD
IEmbyApiFactory embyApi, ISettingsService<EmbySettings> embySettings,
IJellyfinApiFactory jellyfinApi, ISettingsService<JellyfinSettings> jellyfinSettings,
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)
{
_plexApi = plexApi;
@ -61,14 +68,49 @@ namespace Ombi.Core.Authentication
_embySettings = embySettings;
_jellyfinSettings = jellyfinSettings;
_authSettings = auth;
_ldapUserManager = ldapUserManager;
_userManagementSettings = userManagementSettings;
}
private readonly IPlexApi _plexApi;
private readonly IEmbyApiFactory _embyApi;
<<<<<<< HEAD
private readonly IJellyfinApiFactory _jellyfinApi;
=======
private readonly ILdapUserManager _ldapUserManager;
>>>>>>> 691c70804f203fab858b1079a1cf3d5e4adbf322
private readonly ISettingsService<EmbySettings> _embySettings;
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
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)
{
@ -90,9 +132,15 @@ namespace Ombi.Core.Authentication
{
return await CheckEmbyPasswordAsync(user, password);
}
<<<<<<< HEAD
if (user.UserType == UserType.JellyfinUser)
{
return await CheckJellyfinPasswordAsync(user, password);
=======
if (user.UserType == UserType.LdapUser)
{
return await CheckLdapPasswordAsync(user, password);
>>>>>>> 691c70804f203fab858b1079a1cf3d5e4adbf322
}
return false;
}
@ -227,5 +275,41 @@ namespace Ombi.Core.Authentication
}
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;
}
}
}

View 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);
}
}

View file

@ -16,6 +16,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.0" />
<PackageReference Include="MusicBrainzAPI" Version="2.0.1" />
<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="Microsoft.AspNetCore.SignalR.Core" Version="1.1.0" />
</ItemGroup>

View file

@ -52,6 +52,7 @@ using Ombi.Schedule.Jobs.Jellyfin;
using Ombi.Schedule.Jobs.Ombi;
using Ombi.Schedule.Jobs.Plex;
using Ombi.Schedule.Jobs.Sonarr;
using Ombi.Schedule.Jobs.Ldap;
using Ombi.Store.Repository.Requests;
using Ombi.Updater;
using Ombi.Api.Telegram;
@ -101,6 +102,7 @@ namespace Ombi.DependencyInjection
services.AddTransient<IMusicSender, MusicSender>();
services.AddTransient<IMassEmailSender, MassEmailSender>();
services.AddTransient<IPlexOAuthManager, PlexOAuthManager>();
services.AddTransient<ILdapUserManager, LdapUserManager>();
services.AddTransient<IVoteEngine, VoteEngine>();
services.AddTransient<IDemoMovieSearchEngine, DemoMovieSearchEngine>();
services.AddTransient<IDemoTvSearchEngine, DemoTvSearchEngine>();
@ -231,6 +233,7 @@ namespace Ombi.DependencyInjection
services.AddTransient<IPlexUserImporter, PlexUserImporter>();
services.AddTransient<IEmbyUserImporter, EmbyUserImporter>();
services.AddTransient<IJellyfinUserImporter, JellyfinUserImporter>();
services.AddTransient<ILdapUserImporter, LdapUserImporter>();
services.AddTransient<IWelcomeEmail, WelcomeEmail>();
services.AddTransient<ICouchPotatoSync, CouchPotatoSync>();
services.AddTransient<IProcessProvider, ProcessProvider>();

View file

@ -0,0 +1,7 @@

namespace Ombi.Schedule.Jobs.Ldap
{
public interface ILdapUserImporter : IBaseJob
{
}
}

View 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);
}
}
}

View file

@ -8,6 +8,7 @@ using Ombi.Schedule.Jobs;
using Ombi.Schedule.Jobs.Couchpotato;
using Ombi.Schedule.Jobs.Emby;
using Ombi.Schedule.Jobs.Jellyfin;
using Ombi.Schedule.Jobs.Ldap;
using Ombi.Schedule.Jobs.Lidarr;
using Ombi.Schedule.Jobs.Ombi;
using Ombi.Schedule.Jobs.Plex;
@ -56,6 +57,7 @@ namespace Ombi.Schedule
await AddDvrApps(s);
await AddSystem(s);
await AddNotifications(s);
await AddLdap(s);
// Run Quartz
await OmbiQuartz.Start();
@ -113,5 +115,9 @@ namespace Ombi.Schedule
{
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));
}
}
}

View 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; }
}
}

View file

@ -8,6 +8,7 @@ namespace Ombi.Settings.Settings.Models
public bool ImportPlexUsers { get; set; }
public bool ImportEmbyUsers { get; set; }
public bool ImportJellyfinUsers { get; set; }
public bool ImportLdapUsers { get; set; }
public int MovieRequestLimit { get; set; }
public int EpisodeRequestLimit { get; set; }
public string DefaultStreamingCountry { get; set; } = "US";

View file

@ -35,5 +35,6 @@ namespace Ombi.Store.Entities
EmbyUser = 3,
EmbyConnectUser = 4,
JellyfinUser = 5,
LdapUser = 6,
}
}

View file

@ -194,6 +194,21 @@ export interface IAuthenticationSettings extends ISettings {
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 {
enabled: boolean;
fontAwesomeIcon: string;
@ -206,6 +221,7 @@ export interface IUserManagementSettings extends ISettings {
importPlexAdmin: boolean;
importEmbyUsers: boolean;
importJellyfinUsers: boolean;
importLdapUsers: boolean;
defaultRoles: string[];
movieRequestLimit: number;
episodeRequestLimit: number;

View file

@ -35,6 +35,10 @@ export class JobService extends ServiceHelpers {
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> {
return this.http.post<boolean>(`${this.url}plexcontentcacher/`, {headers: this.headers});
}

View file

@ -39,6 +39,7 @@ import {
IVoteSettings,
ITwilioSettings,
IWebhookNotificationSettings,
ILdapSettings,
} from "../interfaces";
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});
}
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
public getLandingPage(): Observable<ILandingPageSettings> {
return this.http.get<ILandingPageSettings>(`${this.url}/LandingPage`, {headers: this.headers});

View 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>

View file

@ -0,0 +1,9 @@
.small-middle-container {
margin: auto;
width: 95%;
margin-top: 10px;
}
mat-checkbox {
margin-right: 10px;
}

View 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");
}
});
}
}

View file

@ -48,6 +48,7 @@ import { UpdateComponent } from "./update/update.component";
import { UserManagementComponent } from "./usermanagement/usermanagement.component";
import { VoteComponent } from "./vote/vote.component";
import { WikiComponent } from "./wiki.component";
import { LdapComponent } from "./ldap/ldap.component";
import { SettingsMenuComponent } from "./settingsmenu.component";
@ -97,6 +98,7 @@ const routes: Routes = [
{ path: "SickRage", component: SickRageComponent, canActivate: [AuthGuard] },
{ path: "Issues", component: IssuesComponent, canActivate: [AuthGuard] },
{ path: "Authentication", component: AuthenticationComponent, canActivate: [AuthGuard] },
{ path: "Ldap", component: LdapComponent, canActivate: [AuthGuard] },
{ path: "Mobile", component: MobileComponent, canActivate: [AuthGuard] },
{ path: "MassEmail", component: MassEmailComponent, canActivate: [AuthGuard] },
{ path: "Newsletter", component: NewsletterComponent, canActivate: [AuthGuard] },
@ -158,6 +160,7 @@ const routes: Routes = [
TelegramComponent,
IssuesComponent,
AuthenticationComponent,
LdapComponent,
MobileComponent,
MassEmailComponent,
NewsletterComponent,

View file

@ -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/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/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/TheMovieDb']"><i class="fas fa-film icon-spacing"></i> The Movie Database</button>
</mat-menu>

View file

@ -45,7 +45,12 @@
<p-autoComplete [(ngModel)]="bannedJellyfinUsers" [suggestions]="filteredJellyfinUsers" [multiple]="true" field="username" (completeMethod)="filterJellyfinList($event)"></p-autoComplete>
</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 class="col-md-6">

View file

@ -12,7 +12,11 @@ export class UserManagementComponent implements OnInit {
public plexEnabled: boolean;
public embyEnabled: boolean;
<<<<<<< HEAD
public jellyfinEnabled: boolean;
=======
public ldapEnabled: boolean;
>>>>>>> 691c70804f203fab858b1079a1cf3d5e4adbf322
public settings: IUserManagementSettings;
public claims: ICheckbox[];
@ -45,7 +49,11 @@ export class UserManagementComponent implements OnInit {
this.settingsService.getUserManagementSettings().subscribe(x => {
this.settings = x;
<<<<<<< HEAD
if(x.importEmbyUsers || x.importJellyfinUsers || x.importPlexUsers) {
=======
if(x.importEmbyUsers || x.importPlexUsers || x.importLdapUsers) {
>>>>>>> 691c70804f203fab858b1079a1cf3d5e4adbf322
this.enableImportButton = true;
}
@ -100,7 +108,11 @@ export class UserManagementComponent implements OnInit {
});
this.settingsService.getPlex().subscribe(x => this.plexEnabled = x.enable);
this.settingsService.getEmby().subscribe(x => this.embyEnabled = x.enable);
<<<<<<< HEAD
this.settingsService.getJellyfin().subscribe(x => this.jellyfinEnabled = x.enable);
=======
this.settingsService.getLdap().subscribe(x => this.ldapEnabled = x.isEnabled);
>>>>>>> 691c70804f203fab858b1079a1cf3d5e4adbf322
}
public submit(): void {
@ -110,9 +122,14 @@ export class UserManagementComponent implements OnInit {
this.settings.defaultRoles = enabledClaims.map((claim) => claim.value);
this.settings.bannedPlexUserIds = this.bannedPlexUsers.map((u) => u.id);
this.settings.bannedEmbyUserIds = this.bannedEmbyUsers.map((u) => u.id);
<<<<<<< HEAD
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.importPlexUsers) {
>>>>>>> 691c70804f203fab858b1079a1cf3d5e4adbf322
this.enableImportButton = true;
}
@ -141,7 +158,11 @@ export class UserManagementComponent implements OnInit {
this.jobService.runPlexImporter().subscribe();
this.jobService.runEmbyImporter().subscribe();
<<<<<<< HEAD
this.jobService.runJellyfinImporter().subscribe();
=======
this.jobService.runLdapImporter().subscribe();
>>>>>>> 691c70804f203fab858b1079a1cf3d5e4adbf322
}
private filter(query: string, users: IUsersModel[]): IUsersModel[] {

View file

@ -90,6 +90,7 @@
<span *ngIf="u.userType === 3">Emby User</span>
<span *ngIf="u.userType === 4">Emby Connect User</span>
<span *ngIf="u.userType === 5">Jellyfin User</span>
<span *ngIf="u.userType === 6">LDAP User</span> </td>
</td>
</ng-container>
@ -136,6 +137,7 @@
</div>
</div>
<<<<<<< HEAD
<mat-form-field appearance="outline" class="full">
<mat-label>Movie Request Limit</mat-label>
<input matInput id="movieRequestLimit" name="movieRequestLimit" [(ngModel)]="bulkMovieLimit">
@ -159,6 +161,23 @@
<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>
</div>
</div>

View file

@ -11,6 +11,7 @@ using Ombi.Schedule.Jobs.Ombi;
using Ombi.Schedule.Jobs.Plex;
using Ombi.Schedule.Jobs.Plex.Interfaces;
using Ombi.Schedule.Jobs.Radarr;
using Ombi.Schedule.Jobs.Ldap;
using Quartz;
namespace Ombi.Controllers.V1
@ -115,6 +116,16 @@ namespace Ombi.Controllers.V1
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>
/// Runs the Plex Content Cacher
/// </summary>

View file

@ -467,6 +467,28 @@ namespace Ombi.Controllers.V1
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>
/// Save the Radarr settings.
/// </summary>

View file

@ -51,8 +51,7 @@ namespace Ombi.Controllers.V1
{
if (!model.UsePlexOAuth)
{
var user = await _userManager.FindByNameAsync(model.Username);
var user = await _userManager.FindUser(model.Username);
if (user == null)
{
// Could this be an email login?
@ -67,7 +66,6 @@ namespace Ombi.Controllers.V1
user.EmailLogin = true;
}
// Verify Password
if (await _userManager.CheckPasswordAsync(user, model.Password))
{
@ -187,18 +185,12 @@ namespace Ombi.Controllers.V1
var account = await _plexOAuthManager.GetAccount(accessToken);
// Get the ombi user
var user = await _userManager.FindByNameAsync(account.user.username);
if (user == null)
{
// Could this be an email login?
user = await _userManager.FindByEmailAsync(account.user.email);
var user = await _userManager.FindUser(account.user.username);
if (user == null)
{
return new UnauthorizedResult();
}
}
return await CreateToken(true, user);
}
@ -228,18 +220,12 @@ namespace Ombi.Controllers.V1
[HttpPost("requirePassword")]
public async Task<bool> DoesUserRequireAPassword([FromBody] UserAuthModel model)
{
var user = await _userManager.FindByNameAsync(model.Username);
if (user == null)
{
// Could this be an email login?
user = await _userManager.FindByEmailAsync(model.Username);
var user = await _userManager.FindUser(model.Username);
if (user == null)
{
return true;
}
}
var requires = await _userManager.RequiresPassword(user);
return requires;