diff --git a/src/Ombi.Settings/Settings/Models/AuthenticationSettings.cs b/src/Ombi.Settings/Settings/Models/AuthenticationSettings.cs index f6736e7c5..ed2775480 100644 --- a/src/Ombi.Settings/Settings/Models/AuthenticationSettings.cs +++ b/src/Ombi.Settings/Settings/Models/AuthenticationSettings.cs @@ -13,5 +13,7 @@ namespace Ombi.Settings.Settings.Models public bool RequireNonAlphanumeric { get; set; } public bool RequireUppercase { get; set; } public bool EnableOAuth { get; set; } // Plex OAuth + public bool EnableHeaderAuth { get; set; } // Header SSO + public string HeaderAuthVariable { get; set; } // Header SSO } } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/auth/auth.service.ts b/src/Ombi/ClientApp/src/app/auth/auth.service.ts index afc0a2491..ad1ff32ac 100644 --- a/src/Ombi/ClientApp/src/app/auth/auth.service.ts +++ b/src/Ombi/ClientApp/src/app/auth/auth.service.ts @@ -28,6 +28,10 @@ export class AuthService extends ServiceHelpers { return this.http.post(`${this.url}/requirePassword`, JSON.stringify(login), { headers: this.headers }); } + public headerAuth(): Observable { + return this.http.post(`${this.url}/header_auth`, {}, { headers: this.headers }); + } + public getToken() { return this.jwtHelperService.tokenGetter(); } diff --git a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts index 8bf2755ec..1dc94f261 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts @@ -238,6 +238,8 @@ export interface IAuthenticationSettings extends ISettings { requireNonAlphanumeric: boolean; requireUppercase: boolean; enableOAuth: boolean; + enableHeaderAuth: boolean; + headerAuthVariable: string; } export interface ICustomPage extends ISettings { diff --git a/src/Ombi/ClientApp/src/app/login/login.component.ts b/src/Ombi/ClientApp/src/app/login/login.component.ts index 235e31a8e..f6f519191 100644 --- a/src/Ombi/ClientApp/src/app/login/login.component.ts +++ b/src/Ombi/ClientApp/src/app/login/login.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit, Inject } from "@angular/core"; +import { Component, OnDestroy, OnInit, Inject } from "@angular/core"; import { FormBuilder, FormGroup, Validators } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { TranslateService } from "@ngx-translate/core"; @@ -106,7 +106,10 @@ export class LoginComponent implements OnDestroy, OnInit { this.settingsService .getAuthentication() - .subscribe((x) => (this.authenticationSettings = x)); + .subscribe((x) => { + this.authenticationSettings = x; + this.headerAuth(); + }); this.settingsService.getClientId().subscribe((x) => (this.clientId = x)); this.images.getRandomBackground().subscribe((x) => { this.background = this.sanitizer.bypassSecurityTrustStyle( @@ -121,7 +124,6 @@ export class LoginComponent implements OnDestroy, OnInit { if (base.length > 1) { this.baseUrl = base; } - this.translate .get("Login.Errors.IncorrectCredentials") .subscribe((x) => (this.errorBody = x)); @@ -255,6 +257,32 @@ export class LoginComponent implements OnDestroy, OnInit { ); } + public headerAuth() { + if (this.authenticationSettings.enableHeaderAuth) { + this.authService.headerAuth().subscribe({ + next: (x) => { + this.store.save("id_token", x.access_token); + + if (this.authService.loggedIn()) { + this.ngOnDestroy(); + this.router.navigate(["/"]); + } else { + this.notify.open(this.errorBody, "OK", { + duration: 3000, + }); + } + }, + error: (e) => { + this.notify.open(this.errorBody, "OK", { + duration: 3000000, + }); + console.error(e); + } + } + ); + } + } + public ngOnDestroy() { clearInterval(this.timer); clearInterval(this.pinTimer); diff --git a/src/Ombi/ClientApp/src/app/settings/authentication/authentication.component.html b/src/Ombi/ClientApp/src/app/settings/authentication/authentication.component.html index 7bc6a4e80..5960c79f7 100644 --- a/src/Ombi/ClientApp/src/app/settings/authentication/authentication.component.html +++ b/src/Ombi/ClientApp/src/app/settings/authentication/authentication.component.html @@ -1,66 +1,46 @@ - +
Authentication
-
-
- - Allow users to login without a password -
-
+
+
+ + Allow users to login without a password + +
+
-
-
- Enable Plex OAuth -
-
+
+
+ Enable Plex OAuth +
+
- - -
-
-
-
-
-
+
+
+
+
+
+
-
\ No newline at end of file + diff --git a/src/Ombi/ClientApp/src/app/settings/authentication/authentication.component.ts b/src/Ombi/ClientApp/src/app/settings/authentication/authentication.component.ts index 6f140f8af..100a502b3 100644 --- a/src/Ombi/ClientApp/src/app/settings/authentication/authentication.component.ts +++ b/src/Ombi/ClientApp/src/app/settings/authentication/authentication.component.ts @@ -1,5 +1,5 @@ -import { Component, OnInit } from "@angular/core"; -import { FormBuilder, FormGroup } from "@angular/forms"; +import { Component, OnInit } from "@angular/core"; +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; import { NotificationService } from "../../services"; import { SettingsService } from "../../services"; @@ -26,8 +26,20 @@ export class AuthenticationComponent implements OnInit { requireNonAlphanumeric: [x.requireNonAlphanumeric], requireUppercase: [x.requireUppercase], enableOAuth: [x.enableOAuth], + enableHeaderAuth: [x.enableHeaderAuth], + headerAuthVariable: [x.headerAuthVariable], + }); + this.form.controls.enableHeaderAuth.valueChanges.subscribe(x => { + if (x) { + this.form.get("headerAuthVariable").setValidators(Validators.required); + } else { + this.form.get("headerAuthVariable").clearValidators(); + } + this.form.get("headerAuthVariable").updateValueAndValidity(); }); }); + + } public onSubmit(form: FormGroup) { diff --git a/src/Ombi/Controllers/V1/TokenController.cs b/src/Ombi/Controllers/V1/TokenController.cs index f9ea57a5c..3a409c206 100644 --- a/src/Ombi/Controllers/V1/TokenController.cs +++ b/src/Ombi/Controllers/V1/TokenController.cs @@ -16,6 +16,8 @@ using Ombi.Models.External; using Ombi.Models.Identity; using Ombi.Store.Entities; using Ombi.Store.Repository; +using Ombi.Core.Settings; +using Ombi.Settings.Settings.Models; namespace Ombi.Controllers.V1 { @@ -25,13 +27,14 @@ namespace Ombi.Controllers.V1 public class TokenController : ControllerBase { public TokenController(OmbiUserManager um, IOptions ta, ITokenRepository token, - IPlexOAuthManager oAuthManager, ILogger logger) + IPlexOAuthManager oAuthManager, ILogger logger, ISettingsService auth) { _userManager = um; _tokenAuthenticationOptions = ta.Value; _token = token; _plexOAuthManager = oAuthManager; _log = logger; + _authSettings = auth; } private readonly TokenAuthentication _tokenAuthenticationOptions; @@ -39,6 +42,7 @@ namespace Ombi.Controllers.V1 private readonly OmbiUserManager _userManager; private readonly IPlexOAuthManager _plexOAuthManager; private readonly ILogger _log; + private readonly ISettingsService _authSettings; /// /// Gets the token. @@ -143,7 +147,6 @@ namespace Ombi.Controllers.V1 var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(StartupSingleton.Instance.SecurityKey)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); - var token = new JwtSecurityToken( claims: claims, expires: rememberMe ? DateTime.Now.AddYears(1) : DateTime.Now.AddDays(7), @@ -272,5 +275,38 @@ namespace Ombi.Controllers.V1 return ip; } + + [HttpPost("header_auth")] + [ProducesResponseType(401)] + [ProducesResponseType(200)] + public async Task HeaderAuth() + { + var authSettings = await _authSettings.GetSettingsAsync(); + _log.LogInformation("Logging with header: " + authSettings.HeaderAuthVariable); + if (authSettings.HeaderAuthVariable != null && authSettings.EnableHeaderAuth) + { + if (Request.HttpContext?.Request?.Headers != null && Request.HttpContext.Request.Headers.ContainsKey(authSettings.HeaderAuthVariable)) + { + var username = Request.HttpContext.Request.Headers[authSettings.HeaderAuthVariable].ToString(); + + // Check if user exists + var user = await _userManager.FindByNameAsync(username); + if (user == null) + { + return new UnauthorizedResult(); + } + + return await CreateToken(true, user); + } + else + { + return new UnauthorizedResult(); + } + } + else + { + return new UnauthorizedResult(); + } + } } }