mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-14 02:26:55 -07:00
The move!
This commit is contained in:
parent
1daf480b1b
commit
25526cc4d9
1147 changed files with 85 additions and 8524 deletions
|
@ -1,414 +0,0 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: CustomAuthenticationProvider.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Nancy;
|
||||
using Nancy.Authentication.Forms;
|
||||
using Nancy.Bootstrapper;
|
||||
using Nancy.Cookies;
|
||||
using Nancy.Cryptography;
|
||||
using Nancy.Extensions;
|
||||
using Nancy.Helpers;
|
||||
using Nancy.Security;
|
||||
using Ombi.Core;
|
||||
|
||||
namespace Ombi.UI.Authentication
|
||||
{
|
||||
public class CustomAuthenticationProvider
|
||||
{
|
||||
private static string formsAuthenticationCookieName = "_ncfa";
|
||||
private static CustomAuthenticationConfiguration currentConfiguration;
|
||||
|
||||
/// <summary>Gets or sets the forms authentication cookie name</summary>
|
||||
public static string FormsAuthenticationCookieName
|
||||
{
|
||||
get
|
||||
{
|
||||
return CustomAuthenticationProvider.formsAuthenticationCookieName;
|
||||
}
|
||||
set
|
||||
{
|
||||
CustomAuthenticationProvider.formsAuthenticationCookieName = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Enables forms authentication for the application</summary>
|
||||
/// <param name="pipelines">Pipelines to add handlers to (usually "this")</param>
|
||||
/// <param name="configuration">Forms authentication configuration</param>
|
||||
public static void Enable(IPipelines pipelines, CustomAuthenticationConfiguration configuration)
|
||||
{
|
||||
if (pipelines == null)
|
||||
throw new ArgumentNullException("pipelines");
|
||||
if (configuration == null)
|
||||
throw new ArgumentNullException("configuration");
|
||||
if (!configuration.IsValid)
|
||||
throw new ArgumentException("Configuration is invalid", "configuration");
|
||||
CustomAuthenticationProvider.currentConfiguration = configuration;
|
||||
pipelines.BeforeRequest.AddItemToStartOfPipeline(CustomAuthenticationProvider.GetLoadAuthenticationHook(configuration));
|
||||
if (configuration.DisableRedirect)
|
||||
return;
|
||||
pipelines.AfterRequest.AddItemToEndOfPipeline(CustomAuthenticationProvider.GetRedirectToLoginHook(configuration));
|
||||
}
|
||||
|
||||
/// <summary>Enables forms authentication for a module</summary>
|
||||
/// <param name="module">Module to add handlers to (usually "this")</param>
|
||||
/// <param name="configuration">Forms authentication configuration</param>
|
||||
public static void Enable(INancyModule module, CustomAuthenticationConfiguration configuration)
|
||||
{
|
||||
if (module == null)
|
||||
throw new ArgumentNullException("module");
|
||||
if (configuration == null)
|
||||
throw new ArgumentNullException("configuration");
|
||||
if (!configuration.IsValid)
|
||||
throw new ArgumentException("Configuration is invalid", "configuration");
|
||||
module.RequiresAuthentication();
|
||||
CustomAuthenticationProvider.currentConfiguration = configuration;
|
||||
module.Before.AddItemToStartOfPipeline(CustomAuthenticationProvider.GetLoadAuthenticationHook(configuration));
|
||||
if (configuration.DisableRedirect)
|
||||
return;
|
||||
module.After.AddItemToEndOfPipeline(CustomAuthenticationProvider.GetRedirectToLoginHook(configuration));
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a response that sets the authentication cookie and redirects
|
||||
/// the user back to where they came from.
|
||||
/// </summary>
|
||||
/// <param name="context">Current context</param>
|
||||
/// <param name="userIdentifier">User identifier guid</param>
|
||||
/// <param name="cookieExpiry">Optional expiry date for the cookie (for 'Remember me')</param>
|
||||
/// <param name="fallbackRedirectUrl">Url to redirect to if none in the querystring</param>
|
||||
/// <returns>Nancy response with redirect.</returns>
|
||||
public static Response UserLoggedInRedirectResponse(NancyContext context, Guid userIdentifier, DateTime? cookieExpiry = null, string fallbackRedirectUrl = null)
|
||||
{
|
||||
var redirectUrl = fallbackRedirectUrl;
|
||||
|
||||
if (string.IsNullOrEmpty(redirectUrl))
|
||||
{
|
||||
redirectUrl = context.Request.Url.BasePath;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(redirectUrl))
|
||||
{
|
||||
redirectUrl = "/";
|
||||
}
|
||||
|
||||
string redirectQuerystringKey = GetRedirectQuerystringKey(currentConfiguration);
|
||||
|
||||
if (context.Request.Query[redirectQuerystringKey].HasValue)
|
||||
{
|
||||
var queryUrl = (string)context.Request.Query[redirectQuerystringKey];
|
||||
|
||||
if (context.IsLocalUrl(queryUrl))
|
||||
{
|
||||
redirectUrl = queryUrl;
|
||||
}
|
||||
}
|
||||
|
||||
var response = context.GetRedirect(redirectUrl);
|
||||
var authenticationCookie = BuildCookie(userIdentifier, cookieExpiry, currentConfiguration);
|
||||
response.WithCookie(authenticationCookie);
|
||||
|
||||
return response;
|
||||
}
|
||||
/// <summary>
|
||||
/// Logs the user in.
|
||||
/// </summary>
|
||||
/// <param name="userIdentifier">User identifier guid</param>
|
||||
/// <param name="cookieExpiry">Optional expiry date for the cookie (for 'Remember me')</param>
|
||||
/// <returns>Nancy response with status <see cref="HttpStatusCode.OK"/></returns>
|
||||
public static Response UserLoggedInResponse(Guid userIdentifier, DateTime? cookieExpiry = null)
|
||||
{
|
||||
var response =
|
||||
(Response)HttpStatusCode.OK;
|
||||
|
||||
var authenticationCookie =
|
||||
BuildCookie(userIdentifier, cookieExpiry, currentConfiguration);
|
||||
|
||||
response.WithCookie(authenticationCookie);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs the user out and redirects them to a URL
|
||||
/// </summary>
|
||||
/// <param name="context">Current context</param>
|
||||
/// <param name="redirectUrl">URL to redirect to</param>
|
||||
/// <returns>Nancy response</returns>
|
||||
public static Response LogOutAndRedirectResponse(NancyContext context, string redirectUrl)
|
||||
{
|
||||
var response = context.GetRedirect(redirectUrl);
|
||||
var authenticationCookie = BuildLogoutCookie(currentConfiguration);
|
||||
response.WithCookie(authenticationCookie);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs the user out.
|
||||
/// </summary>
|
||||
/// <returns>Nancy response</returns>
|
||||
public static Response LogOutResponse()
|
||||
{
|
||||
var response =
|
||||
(Response)HttpStatusCode.OK;
|
||||
|
||||
var authenticationCookie =
|
||||
BuildLogoutCookie(currentConfiguration);
|
||||
|
||||
response.WithCookie(authenticationCookie);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pre request hook for loading the authenticated user's details
|
||||
/// from the cookie.
|
||||
/// </summary>
|
||||
/// <param name="configuration">Forms authentication configuration to use</param>
|
||||
/// <returns>Pre request hook delegate</returns>
|
||||
private static Func<NancyContext, Response> GetLoadAuthenticationHook(CustomAuthenticationConfiguration configuration)
|
||||
{
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException("configuration");
|
||||
}
|
||||
|
||||
return context =>
|
||||
{
|
||||
var userGuid = GetAuthenticatedUserFromCookie(context, configuration);
|
||||
|
||||
if (userGuid != Guid.Empty)
|
||||
{
|
||||
var identity = new UserIdentity();
|
||||
|
||||
var plexUsers = configuration.PlexUserRepository.GetAll();
|
||||
var plexUser = plexUsers.FirstOrDefault(x => Guid.Parse(x.LoginId) == userGuid);
|
||||
var embyUsers = configuration.EmbyUserRepository.GetAll();
|
||||
var embyUser = embyUsers.FirstOrDefault(x => Guid.Parse(x.LoginId) == userGuid);
|
||||
|
||||
if (plexUser != null)
|
||||
{
|
||||
identity.UserName = plexUser.Username;
|
||||
}
|
||||
if (embyUser != null)
|
||||
{
|
||||
identity.UserName = embyUser.Username;
|
||||
}
|
||||
|
||||
var localUsers = configuration.LocalUserRepository.GetAll();
|
||||
|
||||
var localUser = localUsers.FirstOrDefault(x => Guid.Parse(x.UserGuid) == userGuid);
|
||||
if (localUser != null)
|
||||
{
|
||||
identity.UserName = localUser.UserName;
|
||||
}
|
||||
|
||||
context.CurrentUser = identity;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the post request hook for redirecting to the login page
|
||||
/// </summary>
|
||||
/// <param name="configuration">Forms authentication configuration to use</param>
|
||||
/// <returns>Post request hook delegate</returns>
|
||||
private static Action<NancyContext> GetRedirectToLoginHook(CustomAuthenticationConfiguration configuration)
|
||||
{
|
||||
return context =>
|
||||
{
|
||||
if (context.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
string redirectQuerystringKey = GetRedirectQuerystringKey(configuration);
|
||||
|
||||
context.Response = context.GetRedirect(
|
||||
string.Format("{0}?{1}={2}",
|
||||
configuration.RedirectUrl,
|
||||
redirectQuerystringKey,
|
||||
context.ToFullPath("~" + context.Request.Path + HttpUtility.UrlEncode(context.Request.Url.Query))));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authenticated user GUID from the incoming request cookie if it exists
|
||||
/// and is valid.
|
||||
/// </summary>
|
||||
/// <param name="context">Current context</param>
|
||||
/// <param name="configuration">Current configuration</param>
|
||||
/// <returns>Returns user guid, or Guid.Empty if not present or invalid</returns>
|
||||
private static Guid GetAuthenticatedUserFromCookie(NancyContext context, CustomAuthenticationConfiguration configuration)
|
||||
{
|
||||
if (!context.Request.Cookies.ContainsKey(formsAuthenticationCookieName))
|
||||
{
|
||||
return Guid.Empty;
|
||||
}
|
||||
|
||||
var cookieValueEncrypted = context.Request.Cookies[formsAuthenticationCookieName];
|
||||
|
||||
if (string.IsNullOrEmpty(cookieValueEncrypted))
|
||||
{
|
||||
return Guid.Empty;
|
||||
}
|
||||
|
||||
var cookieValue = DecryptAndValidateAuthenticationCookie(cookieValueEncrypted, configuration);
|
||||
|
||||
Guid returnGuid;
|
||||
if (string.IsNullOrEmpty(cookieValue) || !Guid.TryParse(cookieValue, out returnGuid))
|
||||
{
|
||||
return Guid.Empty;
|
||||
}
|
||||
|
||||
return returnGuid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build the forms authentication cookie
|
||||
/// </summary>
|
||||
/// <param name="userIdentifier">Authenticated user identifier</param>
|
||||
/// <param name="cookieExpiry">Optional expiry date for the cookie (for 'Remember me')</param>
|
||||
/// <param name="configuration">Current configuration</param>
|
||||
/// <returns>Nancy cookie instance</returns>
|
||||
private static INancyCookie BuildCookie(Guid userIdentifier, DateTime? cookieExpiry, CustomAuthenticationConfiguration configuration)
|
||||
{
|
||||
var cookieContents = EncryptAndSignCookie(userIdentifier.ToString(), configuration);
|
||||
|
||||
var cookie = new NancyCookie(formsAuthenticationCookieName, cookieContents, true, configuration.RequiresSSL, cookieExpiry);
|
||||
|
||||
if (!string.IsNullOrEmpty(configuration.Domain))
|
||||
{
|
||||
cookie.Domain = configuration.Domain;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(configuration.Path))
|
||||
{
|
||||
cookie.Path = configuration.Path;
|
||||
}
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a cookie for logging a user out
|
||||
/// </summary>
|
||||
/// <param name="configuration">Current configuration</param>
|
||||
/// <returns>Nancy cookie instance</returns>
|
||||
private static INancyCookie BuildLogoutCookie(CustomAuthenticationConfiguration configuration)
|
||||
{
|
||||
var cookie = new NancyCookie(formsAuthenticationCookieName, String.Empty, true, configuration.RequiresSSL, DateTime.Now.AddDays(-1));
|
||||
|
||||
if (!string.IsNullOrEmpty(configuration.Domain))
|
||||
{
|
||||
cookie.Domain = configuration.Domain;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(configuration.Path))
|
||||
{
|
||||
cookie.Path = configuration.Path;
|
||||
}
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt and sign the cookie contents
|
||||
/// </summary>
|
||||
/// <param name="cookieValue">Plain text cookie value</param>
|
||||
/// <param name="configuration">Current configuration</param>
|
||||
/// <returns>Encrypted and signed string</returns>
|
||||
private static string EncryptAndSignCookie(string cookieValue, CustomAuthenticationConfiguration configuration)
|
||||
{
|
||||
var encryptedCookie = configuration.CryptographyConfiguration.EncryptionProvider.Encrypt(cookieValue);
|
||||
var hmacBytes = GenerateHmac(encryptedCookie, configuration);
|
||||
var hmacString = Convert.ToBase64String(hmacBytes);
|
||||
|
||||
return String.Format("{1}{0}", encryptedCookie, hmacString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a hmac for the encrypted cookie string
|
||||
/// </summary>
|
||||
/// <param name="encryptedCookie">Encrypted cookie string</param>
|
||||
/// <param name="configuration">Current configuration</param>
|
||||
/// <returns>Hmac byte array</returns>
|
||||
private static byte[] GenerateHmac(string encryptedCookie, CustomAuthenticationConfiguration configuration)
|
||||
{
|
||||
return configuration.CryptographyConfiguration.HmacProvider.GenerateHmac(encryptedCookie);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt and validate an encrypted and signed cookie value
|
||||
/// </summary>
|
||||
/// <param name="cookieValue">Encrypted and signed cookie value</param>
|
||||
/// <param name="configuration">Current configuration</param>
|
||||
/// <returns>Decrypted value, or empty on error or if failed validation</returns>
|
||||
public static string DecryptAndValidateAuthenticationCookie(string cookieValue, CustomAuthenticationConfiguration configuration)
|
||||
{
|
||||
var hmacStringLength = Base64Helpers.GetBase64Length(configuration.CryptographyConfiguration.HmacProvider.HmacLength);
|
||||
|
||||
var encryptedCookie = cookieValue.Substring(hmacStringLength);
|
||||
var hmacString = cookieValue.Substring(0, hmacStringLength);
|
||||
|
||||
var encryptionProvider = configuration.CryptographyConfiguration.EncryptionProvider;
|
||||
|
||||
// Check the hmacs, but don't early exit if they don't match
|
||||
var hmacBytes = Convert.FromBase64String(hmacString);
|
||||
var newHmac = GenerateHmac(encryptedCookie, configuration);
|
||||
var hmacValid = HmacComparer.Compare(newHmac, hmacBytes, configuration.CryptographyConfiguration.HmacProvider.HmacLength);
|
||||
|
||||
var decrypted = encryptionProvider.Decrypt(encryptedCookie);
|
||||
|
||||
// Only return the decrypted result if the hmac was ok
|
||||
return hmacValid ? decrypted : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the redirect query string key from <see cref="FormsAuthenticationConfiguration"/>
|
||||
/// </summary>
|
||||
/// <param name="configuration">The forms authentication configuration.</param>
|
||||
/// <returns>Redirect Querystring key</returns>
|
||||
private static string GetRedirectQuerystringKey(CustomAuthenticationConfiguration configuration)
|
||||
{
|
||||
string redirectQuerystringKey = null;
|
||||
|
||||
if (configuration != null)
|
||||
{
|
||||
redirectQuerystringKey = configuration.RedirectQuerystringKey;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(redirectQuerystringKey))
|
||||
{
|
||||
redirectQuerystringKey = CustomAuthenticationConfiguration.DefaultRedirectQuerystringKey;
|
||||
}
|
||||
|
||||
return redirectQuerystringKey;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue