Merge pull request #4510 from fservida/sso

SSO with Header Authentication
pull/4532/head
Jamie 2 years ago committed by GitHub
commit 71aa74ddcc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -13,5 +13,8 @@ namespace Ombi.Settings.Settings.Models
public bool RequireNonAlphanumeric { get; set; } public bool RequireNonAlphanumeric { get; set; }
public bool RequireUppercase { get; set; } public bool RequireUppercase { get; set; }
public bool EnableOAuth { get; set; } // Plex OAuth public bool EnableOAuth { get; set; } // Plex OAuth
public bool EnableHeaderAuth { get; set; } // Header SSO
public string HeaderAuthVariable { get; set; } // Header SSO
} }
} }

@ -28,6 +28,10 @@ export class AuthService extends ServiceHelpers {
return this.http.post<boolean>(`${this.url}/requirePassword`, JSON.stringify(login), { headers: this.headers }); return this.http.post<boolean>(`${this.url}/requirePassword`, JSON.stringify(login), { headers: this.headers });
} }
public headerAuth(): Observable<any> {
return this.http.post<boolean>(`${this.url}/header_auth`, {}, { headers: this.headers });
}
public getToken() { public getToken() {
return this.jwtHelperService.tokenGetter(); return this.jwtHelperService.tokenGetter();
} }

@ -238,6 +238,8 @@ export interface IAuthenticationSettings extends ISettings {
requireNonAlphanumeric: boolean; requireNonAlphanumeric: boolean;
requireUppercase: boolean; requireUppercase: boolean;
enableOAuth: boolean; enableOAuth: boolean;
enableHeaderAuth: boolean;
headerAuthVariable: string;
} }
export interface ICustomPage extends ISettings { export interface ICustomPage extends ISettings {

@ -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 { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
@ -106,7 +106,7 @@ export class LoginComponent implements OnDestroy, OnInit {
this.settingsService this.settingsService
.getAuthentication() .getAuthentication()
.subscribe((x) => (this.authenticationSettings = x)); .subscribe((x) => { this.authenticationSettings = x; this.headerAuth(); });
this.settingsService.getClientId().subscribe((x) => (this.clientId = x)); this.settingsService.getClientId().subscribe((x) => (this.clientId = x));
this.images.getRandomBackground().subscribe((x) => { this.images.getRandomBackground().subscribe((x) => {
this.background = this.sanitizer.bypassSecurityTrustStyle( this.background = this.sanitizer.bypassSecurityTrustStyle(
@ -121,7 +121,6 @@ export class LoginComponent implements OnDestroy, OnInit {
if (base.length > 1) { if (base.length > 1) {
this.baseUrl = base; this.baseUrl = base;
} }
this.translate this.translate
.get("Login.Errors.IncorrectCredentials") .get("Login.Errors.IncorrectCredentials")
.subscribe((x) => (this.errorBody = x)); .subscribe((x) => (this.errorBody = x));
@ -255,6 +254,31 @@ export class LoginComponent implements OnDestroy, OnInit {
); );
} }
public headerAuth() {
if (this.authenticationSettings.enableHeaderAuth) {
this.authService.headerAuth().subscribe(
(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,
});
}
},
(err) => {
this.notify.open(this.errorBody, "OK", {
duration: 3000000,
});
}
);
}
}
public ngOnDestroy() { public ngOnDestroy() {
clearInterval(this.timer); clearInterval(this.timer);
clearInterval(this.pinTimer); clearInterval(this.pinTimer);

@ -1,66 +1,81 @@
<settings-menu></settings-menu> <settings-menu></settings-menu>
<div class="small-middle-container"> <div class="small-middle-container">
<wiki></wiki> <wiki></wiki>
<fieldset *ngIf="form"> <fieldset *ngIf="form">
<legend>Authentication</legend> <legend>Authentication</legend>
<div class="md-form-field" style="margin-top:1em;"></div> <div class="md-form-field" style="margin-top:1em;"></div>
<form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)"> <form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)">
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
<mat-slide-toggle id="allowNoPassword" name="allowNoPassword" formControlName="allowNoPassword"> <mat-slide-toggle id="allowNoPassword" name="allowNoPassword" formControlName="allowNoPassword">
Allow users to login without a password</mat-slide-toggle> Allow users to login without a password
</div> </mat-slide-toggle>
</div> </div>
</div>
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
<mat-slide-toggle id="enableOAuth" name="enableOAuth" formControlName="enableOAuth">Enable Plex OAuth</mat-slide-toggle> <mat-slide-toggle id="enableOAuth" name="enableOAuth" formControlName="enableOAuth">Enable Plex OAuth</mat-slide-toggle>
</div> </div>
</div> </div>
<!-- <hr/> <div class="form-group">
<div class="form-group"> <div class="checkbox">
<div class="checkbox"> <mat-slide-toggle id="enableHeaderAuth" name="enableHeaderAuth" formControlName="enableHeaderAuth">Enable Authentication with Header Variable</mat-slide-toggle>
<input type="checkbox" id="requiredDigit" name="requiredDigit" formControlName="requiredDigit"> </div>
<label for="requiredDigit">Require a digit in the password</label> </div>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label for="requiredLength" class="control-label">Required password length</label> <label for="headerAuthVariable" class="control-label">Header Authentication Variable</label>
<div> <div>
<input type="text" class="form-control form-control-custom " id="requiredLength" name="requiredLength" formControlName="requiredLength"> <input type="text" class="form-control form-control-custom " id="headerAuthVariable" name="headerAuthVariable" formControlName="headerAuthVariable">
</div> </div>
</div> </div>
<div class="form-group"> <!-- <hr/>
<div class="checkbox"> <div class="form-group">
<input type="checkbox" id="requiredLowercase" name="requiredLowercase" formControlName="requiredLowercase"> <div class="checkbox">
<label for="requiredLowercase">Require a lowercase character in the password</label> <input type="checkbox" id="requiredDigit" name="requiredDigit" formControlName="requiredDigit">
</div> <label for="requiredDigit">Require a digit in the password</label>
</div> </div>
</div>
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <label for="requiredLength" class="control-label">Required password length</label>
<input type="checkbox" id="requireNonAlphanumeric" name="requireNonAlphanumeric" formControlName="requireNonAlphanumeric"> <div>
<label for="requireNonAlphanumeric">Require a NonAlphanumeric character in the password</label> <input type="text" class="form-control form-control-custom " id="requiredLength" name="requiredLength" formControlName="requiredLength">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
<input type="checkbox" id="requireUppercase" name="requireUppercase" formControlName="requireUppercase"> <input type="checkbox" id="requiredLowercase" name="requiredLowercase" formControlName="requiredLowercase">
<label for="requireUppercase">Require a uppercase character in the password</label> <label for="requiredLowercase">Require a lowercase character in the password</label>
</div> </div>
</div> --> </div>
<div class="form-group"> <div class="form-group">
<div> <div class="checkbox">
<button mat-raised-button type="submit" color="primary" [disabled]="form.invalid" class="mat-focus-indicator mat-stroked-button accent mat-accent mat-raised-button mat-button-base" ng-reflect-disabled="false"> <input type="checkbox" id="requireNonAlphanumeric" name="requireNonAlphanumeric" formControlName="requireNonAlphanumeric">
<span class="mat-button-wrapper">Submit</span><div matripple="" class="mat-ripple mat-button-ripple" ng-reflect-disabled="false" ng-reflect-centered="false" ng-reflect-trigger="[object HTMLButtonElement]"></div> <label for="requireNonAlphanumeric">Require a NonAlphanumeric character in the password</label>
<div class="mat-button-focus-overlay"></div></button><div class="md-form-field" style="margin-top:1em;"></div> </div>
</div> </div>
</div>
</form> <div class="form-group">
<div class="checkbox">
<input type="checkbox" id="requireUppercase" name="requireUppercase" formControlName="requireUppercase">
<label for="requireUppercase">Require a uppercase character in the password</label>
</div>
</div> -->
<div class="form-group">
<div>
<button mat-raised-button type="submit" color="primary" [disabled]="form.invalid" class="mat-focus-indicator mat-stroked-button accent mat-accent mat-raised-button mat-button-base" ng-reflect-disabled="false">
<span class="mat-button-wrapper">Submit</span><div matripple="" class="mat-ripple mat-button-ripple" ng-reflect-disabled="false" ng-reflect-centered="false" ng-reflect-trigger="[object HTMLButtonElement]"></div>
<div class="mat-button-focus-overlay"></div>
</button><div class="md-form-field" style="margin-top:1em;"></div>
</div>
</div>
</form>
</fieldset> </fieldset>
</div> </div>

@ -1,4 +1,4 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { FormBuilder, FormGroup } from "@angular/forms"; import { FormBuilder, FormGroup } from "@angular/forms";
import { NotificationService } from "../../services"; import { NotificationService } from "../../services";
@ -26,6 +26,8 @@ export class AuthenticationComponent implements OnInit {
requireNonAlphanumeric: [x.requireNonAlphanumeric], requireNonAlphanumeric: [x.requireNonAlphanumeric],
requireUppercase: [x.requireUppercase], requireUppercase: [x.requireUppercase],
enableOAuth: [x.enableOAuth], enableOAuth: [x.enableOAuth],
enableHeaderAuth: [x.enableHeaderAuth],
headerAuthVariable: [x.headerAuthVariable],
}); });
}); });
} }

@ -16,6 +16,8 @@ using Ombi.Models.External;
using Ombi.Models.Identity; using Ombi.Models.Identity;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Ombi.Core.Settings;
using Ombi.Settings.Settings.Models;
namespace Ombi.Controllers.V1 namespace Ombi.Controllers.V1
{ {
@ -25,13 +27,14 @@ namespace Ombi.Controllers.V1
public class TokenController : ControllerBase public class TokenController : ControllerBase
{ {
public TokenController(OmbiUserManager um, IOptions<TokenAuthentication> ta, ITokenRepository token, public TokenController(OmbiUserManager um, IOptions<TokenAuthentication> ta, ITokenRepository token,
IPlexOAuthManager oAuthManager, ILogger<TokenController> logger) IPlexOAuthManager oAuthManager, ILogger<TokenController> logger, ISettingsService<AuthenticationSettings> auth)
{ {
_userManager = um; _userManager = um;
_tokenAuthenticationOptions = ta.Value; _tokenAuthenticationOptions = ta.Value;
_token = token; _token = token;
_plexOAuthManager = oAuthManager; _plexOAuthManager = oAuthManager;
_log = logger; _log = logger;
_authSettings = auth;
} }
private readonly TokenAuthentication _tokenAuthenticationOptions; private readonly TokenAuthentication _tokenAuthenticationOptions;
@ -39,6 +42,7 @@ namespace Ombi.Controllers.V1
private readonly OmbiUserManager _userManager; private readonly OmbiUserManager _userManager;
private readonly IPlexOAuthManager _plexOAuthManager; private readonly IPlexOAuthManager _plexOAuthManager;
private readonly ILogger<TokenController> _log; private readonly ILogger<TokenController> _log;
private readonly ISettingsService<AuthenticationSettings> _authSettings;
/// <summary> /// <summary>
/// Gets the token. /// Gets the token.
@ -272,5 +276,39 @@ namespace Ombi.Controllers.V1
return ip; return ip;
} }
[HttpPost("header_auth")]
[ProducesResponseType(401)]
public async Task<IActionResult> HeaderAuth()
{
string username = null;
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))
{
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();
}
}
} }
} }

Loading…
Cancel
Save