using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Net ;
using System.Threading.Tasks ;
using AutoMapper ;
using Microsoft.AspNetCore.Authorization ;
using Microsoft.AspNetCore.Identity ;
using Microsoft.AspNetCore.Mvc ;
using Microsoft.EntityFrameworkCore ;
using Microsoft.Extensions.Logging ;
using Ombi.Api.Plex ;
using Ombi.Api.TheMovieDb.Models ;
using Ombi.Attributes ;
using Ombi.Core.Authentication ;
using Ombi.Core.Engine ;
using Ombi.Core.Engine.Interfaces ;
using Ombi.Core.Helpers ;
using Ombi.Core.Models.UI ;
using Ombi.Core.Services ;
using Ombi.Core.Settings ;
using Ombi.Core.Settings.Models.External ;
using Ombi.Helpers ;
using Ombi.Models ;
using Ombi.Models.Identity ;
using Ombi.Notifications ;
using Ombi.Notifications.Models ;
using Ombi.Schedule.Jobs.Ombi ;
using Ombi.Settings.Settings.Models ;
using Ombi.Settings.Settings.Models.Notifications ;
using Ombi.Store.Entities ;
using Ombi.Store.Entities.Requests ;
using Ombi.Store.Repository ;
using Ombi.Store.Repository.Requests ;
using IdentityResult = Microsoft . AspNetCore . Identity . IdentityResult ;
using OmbiIdentityResult = Ombi . Models . Identity . IdentityResult ;
namespace Ombi.Controllers.V1
{
/// <inheritdoc />
/// <summary>
/// The Identity Controller, the API for everything Identity/User related
/// </summary>
[ApiV1]
[Produces("application/json")]
[ApiController]
public class IdentityController : BaseController
{
public IdentityController ( OmbiUserManager user ,
RoleManager < IdentityRole > rm ,
IEmailProvider prov ,
ISettingsService < EmailNotificationSettings > s ,
ISettingsService < CustomizationSettings > c ,
ISettingsService < OmbiSettings > ombiSettings ,
IWelcomeEmail welcome ,
ILogger < IdentityController > l ,
IPlexApi plexApi ,
ISettingsService < PlexSettings > settings ,
ISettingsService < UserManagementSettings > umSettings ,
IRepository < UserNotificationPreferences > notificationPreferences ,
IRepository < UserQualityProfiles > userProfiles ,
IUserDeletionEngine deletionEngine ,
IRequestLimitService requestLimitService ,
ICacheService cacheService )
{
UserManager = user ;
RoleManager = rm ;
EmailProvider = prov ;
EmailSettings = s ;
CustomizationSettings = c ;
WelcomeEmail = welcome ;
_log = l ;
_plexApi = plexApi ;
_plexSettings = settings ;
OmbiSettings = ombiSettings ;
_userManagementSettings = umSettings ;
_userNotificationPreferences = notificationPreferences ;
_userQualityProfiles = userProfiles ;
_deletionEngine = deletionEngine ;
_requestLimitService = requestLimitService ;
_cacheService = cacheService ;
}
private OmbiUserManager UserManager { get ; }
private readonly IUserDeletionEngine _deletionEngine ;
private readonly IRequestLimitService _requestLimitService ;
private readonly ICacheService _cacheService ;
private RoleManager < IdentityRole > RoleManager { get ; }
private IEmailProvider EmailProvider { get ; }
private ISettingsService < EmailNotificationSettings > EmailSettings { get ; }
private ISettingsService < CustomizationSettings > CustomizationSettings { get ; }
private readonly ISettingsService < UserManagementSettings > _userManagementSettings ;
private ISettingsService < OmbiSettings > OmbiSettings { get ; }
private IWelcomeEmail WelcomeEmail { get ; }
private readonly ILogger < IdentityController > _log ;
private readonly IPlexApi _plexApi ;
private readonly ISettingsService < PlexSettings > _plexSettings ;
private readonly IRepository < UserNotificationPreferences > _userNotificationPreferences ;
private readonly IRepository < UserQualityProfiles > _userQualityProfiles ;
/// <summary>
/// This is what the Wizard will call when creating the user for the very first time.
/// This should never be called after this.
/// The reason why we return false if users exists is that this method doesn't have any
/// authorization and could be called from anywhere.
/// </summary>
/// <remarks>We have [AllowAnonymous] since when going through the wizard we do not have a JWT Token yet</remarks>
/// <param name="user"></param>
/// <returns></returns>
[HttpPost("Wizard")]
[ApiExplorerSettings(IgnoreApi = true)]
[AllowAnonymous]
public async Task < SaveWizardResult > CreateWizardUser ( [ FromBody ] CreateUserWizardModel user )
{
var users = UserManager . Users ;
// There could be a SINGLE plex user as you can create that in the wizard flow, but there should not be anything else
var plexUsersCount = await users . CountAsync ( x = > x . UserType = = UserType . PlexUser ) ;
if ( plexUsersCount > 1 | | users . Any ( x = > x . UserType = = UserType . LocalUser ) )
{
// No one should be calling this. Only the wizard
return new SaveWizardResult { Result = false , Errors = new List < string > { "Looks like there is an existing user!" } } ;
}
if ( user . UsePlexAdminAccount )
{
var settings = await _plexSettings . GetSettingsAsync ( ) ;
var authToken = settings . Servers . FirstOrDefault ( ) ? . PlexAuthToken ? ? string . Empty ;
if ( authToken . IsNullOrEmpty ( ) )
{
_log . LogWarning ( "Could not find an auth token to create the plex user with" ) ;
return new SaveWizardResult { Result = false } ;
}
var plexUser = await _plexApi . GetAccount ( authToken ) ;
var adminUser = new OmbiUser
{
UserName = plexUser . user . username ,
UserType = UserType . PlexUser ,
Email = plexUser . user . email ,
ProviderUserId = plexUser . user . id ,
StreamingCountry = "US" // Default
} ;
await _userManagementSettings . SaveSettingsAsync ( new UserManagementSettings
{
ImportPlexAdmin = true
} ) ;
return await SaveWizardUser ( user , adminUser , false ) ;
}
var userToCreate = new OmbiUser
{
UserName = user . Username ,
UserType = UserType . LocalUser ,
StreamingCountry = "US"
} ;
return await SaveWizardUser ( user , userToCreate , true ) ;
}
private async Task < SaveWizardResult > SaveWizardUser ( CreateUserWizardModel user , OmbiUser userToCreate , bool completeWizard )
{
IdentityResult result ;
var retVal = new SaveWizardResult ( ) ;
// When creating the admin as the plex user, we do not pass in the password.
if ( user . Password . HasValue ( ) )
{
result = await UserManager . CreateAsync ( userToCreate , user . Password ) ;
}
else
{
result = await UserManager . CreateAsync ( userToCreate ) ;
}
if ( result . Succeeded )
{
_log . LogInformation ( "Created User {0}" , userToCreate . UserName ) ;
await CreateRoles ( ) ;
_log . LogInformation ( "Created the roles" ) ;
var roleResult = await UserManager . AddToRoleAsync ( userToCreate , OmbiRoles . Admin ) ;
if ( ! roleResult . Succeeded )
{
LogErrors ( roleResult ) ;
}
else
{
_log . LogInformation ( "Added the Admin role" ) ;
}
if ( completeWizard )
{
// Update the wizard flag
var settings = await OmbiSettings . GetSettingsAsync ( ) ;
settings . Wizard = true ;
await OmbiSettings . SaveSettingsAsync ( settings ) ;
}
}
if ( ! result . Succeeded )
{
LogErrors ( result ) ;
retVal . Errors . AddRange ( result . Errors . Select ( x = > x . Description ) ) ;
}
retVal . Result = result . Succeeded ;
return retVal ;
}
private void LogErrors ( IdentityResult result )
{
foreach ( var err in result . Errors )
{
_log . LogCritical ( err . Description ) ;
}
}
private async Task CreateRoles ( )
{
await CreateRole ( OmbiRoles . AutoApproveMovie ) ;
await CreateRole ( OmbiRoles . Admin ) ;
await CreateRole ( OmbiRoles . AutoApproveTv ) ;
await CreateRole ( OmbiRoles . AutoApproveMusic ) ;
await CreateRole ( OmbiRoles . RequestMusic ) ;
await CreateRole ( OmbiRoles . PowerUser ) ;
await CreateRole ( OmbiRoles . RequestMovie ) ;
await CreateRole ( OmbiRoles . RequestTv ) ;
await CreateRole ( OmbiRoles . Disabled ) ;
await CreateRole ( OmbiRoles . ReceivesNewsletter ) ;
await CreateRole ( OmbiRoles . ManageOwnRequests ) ;
await CreateRole ( OmbiRoles . EditCustomPage ) ;
await CreateRole ( OmbiRoles . EditCustomPage ) ;
await CreateRole ( OmbiRoles . Request4KMovie ) ;
await CreateRole ( OmbiRoles . AutoApprove4KMovie ) ;
}
private async Task CreateRole ( string role )
{
if ( ! await RoleManager . RoleExistsAsync ( role ) )
{
await RoleManager . CreateAsync ( new IdentityRole ( role ) ) ;
}
}
/// <summary>
/// Gets all users.
/// </summary>
/// <returns>Information about all users</returns>
[HttpGet("Users")]
[PowerUser]
public async Task < IEnumerable < UserViewModel > > GetAllUsers ( )
{
var users = await UserManager . Users . Where ( x = > x . UserType ! = UserType . SystemUser )
. ToListAsync ( ) ;
var model = new List < UserViewModel > ( ) ;
foreach ( var user in users )
{
model . Add ( await GetUserWithRoles ( user ) ) ;
}
return model . OrderBy ( x = > x . UserName ) ;
}
/// <summary>
/// Gets all users for dropdown purposes.
/// </summary>
/// <returns>Basic Information about all users</returns>
[HttpGet("dropdown/Users")]
[PowerUser]
public async Task < IEnumerable < UserViewModelDropdown > > GetAllUsersDropdown ( )
{
var users = await _cacheService . GetOrAddAsync ( CacheKeys . UsersDropdown ,
( ) = > UserManager . Users . Where ( x = > x . UserType ! = UserType . SystemUser ) . ToListAsync ( ) ) ;
var model = new List < UserViewModelDropdown > ( ) ;
foreach ( var user in users )
{
model . Add ( new UserViewModelDropdown
{
Id = user . Id ,
Username = user . UserName ,
Email = user . Email
} ) ;
}
return model . OrderBy ( x = > x . Username ) ;
}
/// <summary>
/// Gets the current logged in user.
/// </summary>
/// <returns>Information about all users</returns>
[HttpGet]
[Authorize]
public async Task < UserViewModel > GetCurrentUser ( )
{
var username = User . Identity . Name . ToUpper ( ) ;
var user = await UserManager . Users . FirstOrDefaultAsync ( x = > x . NormalizedUserName = = username ) ;
return await GetUserWithRoles ( user ) ;
}
/// <summary>
/// Sets the current users language
/// </summary>
[HttpPost("language")]
[Authorize]
public async Task < IActionResult > SetCurrentUserLanguage ( [ FromBody ] UserLanguage model )
{
var username = User . Identity . Name . ToUpper ( ) ;
var user = await UserManager . Users . FirstOrDefaultAsync ( x = > x . NormalizedUserName = = username ) ;
user . Language = model . Lang ;
await UserManager . UpdateAsync ( user ) ;
return Ok ( ) ;
}
/// <summary>
/// Returns the supported country codes that we have streaming data for
/// </summary>
[HttpGet("streamingcountry")]
[Authorize]
public IActionResult GetSupportedStreamingCountries ( )
{
var resultsProp = typeof ( Results ) . GetProperties ( ) ;
return Json ( resultsProp . Select ( x = > x . Name ) ) ;
}
/// <summary>
/// Sets the current users country streaming preference
/// </summary>
[HttpPost("streamingcountry")]
[Authorize]
public async Task < IActionResult > SetCurrentUserCountryStreaming ( [ FromBody ] CountryStreamingPreference model )
{
var username = User . Identity . Name . ToUpper ( ) ;
var user = await UserManager . Users . FirstOrDefaultAsync ( x = > x . NormalizedUserName = = username ) ;
user . StreamingCountry = model . Code ;
await UserManager . UpdateAsync ( user ) ;
return Ok ( ) ;
}
/// <summary>
/// Gets the user by the user id.
/// </summary>
/// <returns>Information about the user</returns>
[HttpGet("User/{id}")]
[PowerUser]
public async Task < UserViewModel > GetUser ( string id )
{
var user = await UserManager . Users . FirstOrDefaultAsync ( x = > x . Id = = id ) ;
return await GetUserWithRoles ( user ) ;
}
private async Task < UserViewModel > GetUserWithRoles ( OmbiUser user )
{
var userRoles = await UserManager . GetRolesAsync ( user ) ;
var vm = new UserViewModel
{
Alias = user . Alias ,
UserName = user . UserName ,
Id = user . Id ,
EmailAddress = user . Email ,
UserType = ( Core . Models . UserType ) ( int ) user . UserType ,
Claims = new List < ClaimCheckboxes > ( ) ,
LastLoggedIn = user . LastLoggedIn ,
HasLoggedIn = user . LastLoggedIn . HasValue ,
EpisodeRequestLimit = user . EpisodeRequestLimit ? ? 0 ,
MovieRequestLimit = user . MovieRequestLimit ? ? 0 ,
MusicRequestLimit = user . MusicRequestLimit ? ? 0 ,
MovieRequestLimitType = user . MovieRequestLimitType ? ? RequestLimitType . Week ,
EpisodeRequestLimitType = user . EpisodeRequestLimitType ? ? RequestLimitType . Week ,
MusicRequestLimitType = user . MusicRequestLimitType ? ? RequestLimitType . Week ,
Language = user . Language ,
StreamingCountry = user . StreamingCountry
} ;
foreach ( var role in userRoles )
{
vm . Claims . Add ( new ClaimCheckboxes
{
Value = role ,
Enabled = true
} ) ;
}
// Add the missing claims
var allRoles = await RoleManager . Roles . ToListAsync ( ) ;
var missing = allRoles . Select ( x = > x . Name ) . Except ( userRoles ) ;
foreach ( var role in missing )
{
vm . Claims . Add ( new ClaimCheckboxes
{
Value = role ,
Enabled = false
} ) ;
}
if ( vm . EpisodeRequestLimit > 0 )
{
vm . EpisodeRequestQuota = await _requestLimitService . GetRemainingTvRequests ( user ) ;
}
if ( vm . MovieRequestLimit > 0 )
{
vm . MovieRequestQuota = await _requestLimitService . GetRemainingMovieRequests ( user ) ;
}
if ( vm . MusicRequestLimit > 0 )
{
vm . MusicRequestQuota = await _requestLimitService . GetRemainingMusicRequests ( user ) ;
}
// Get the quality profiles
vm . UserQualityProfiles = await _userQualityProfiles . GetAll ( ) . FirstOrDefaultAsync ( x = > x . UserId = = user . Id ) ;
if ( vm . UserQualityProfiles = = null )
{
vm . UserQualityProfiles = new UserQualityProfiles
{
UserId = user . Id
} ;
}
return vm ;
}
/// <summary>
/// Creates the user.
/// </summary>
/// <param name = "user" > The user.</param>
/// <returns></returns>
[HttpPost]
[PowerUser]
public async Task < OmbiIdentityResult > CreateUser ( [ FromBody ] UserViewModel user )
{
if ( ! EmailValidator . IsValidEmail ( user . EmailAddress ) )
{
return Error ( $"The email address {user.EmailAddress} is not a valid format" ) ;
}
if ( ! CanModifyUser ( user . Claims . Select ( x = > x . Value ) ) )
{
return Error ( "You do not have the correct permissions to create this user" ) ;
}
if ( user . EmailAddress . HasValue ( ) & & await UserManager . FindByEmailAsync ( user . EmailAddress ) ! = null )
{
return Error ( "This email has already been taken" ) ;
}
var ombiUser = new OmbiUser
{
Alias = user . Alias ,
Email = user . EmailAddress ,
UserName = user . UserName ,
UserType = UserType . LocalUser ,
MovieRequestLimit = user . MovieRequestLimit ,
EpisodeRequestLimit = user . EpisodeRequestLimit ,
MusicRequestLimit = user . MusicRequestLimit ,
UserAccessToken = Guid . NewGuid ( ) . ToString ( "N" ) ,
StreamingCountry = user . StreamingCountry . HasValue ( ) ? user . StreamingCountry : "US"
} ;
var userResult = await UserManager . CreateAsync ( ombiUser , user . Password ) ;
if ( ! userResult . Succeeded )
{
// We did not create the user
return new OmbiIdentityResult
{
Errors = userResult . Errors . Select ( x = > x . Description ) . ToList ( )
} ;
}
var roleResult = await AddRoles ( user . Claims , ombiUser ) ;
if ( roleResult . Any ( x = > ! x . Succeeded ) )
{
var messages = new List < string > ( ) ;
foreach ( var errors in roleResult . Where ( x = > ! x . Succeeded ) )
{
messages . AddRange ( errors . Errors . Select ( x = > x . Description ) . ToList ( ) ) ;
}
return new OmbiIdentityResult
{
Errors = messages
} ;
}
// Add the quality profiles
if ( user . UserQualityProfiles ! = null )
{
user . UserQualityProfiles . UserId = ombiUser . Id ;
await _userQualityProfiles . Add ( user . UserQualityProfiles ) ;
}
else
{
user . UserQualityProfiles = new UserQualityProfiles
{
UserId = ombiUser . Id
} ;
}
return new OmbiIdentityResult
{
Successful = true
} ;
}
private bool CanModifyUser ( IEnumerable < string > roles )
{
if ( roles . Any ( x = > x . Equals ( "admin" , StringComparison . CurrentCultureIgnoreCase ) ) )
{
// Only Admins can create admins
if ( ! User . IsInRole ( OmbiRoles . Admin ) )
{
return false ;
}
}
return true ;
}
/// <summary>
/// This is for the local user to change their details.
/// </summary>
/// <param name="ui"></param>
/// <returns></returns>
[HttpPut("local")]
[Authorize]
public async Task < OmbiIdentityResult > UpdateLocalUser ( [ FromBody ] UpdateLocalUserModel ui )
{
if ( string . IsNullOrEmpty ( ui . CurrentPassword ) )
{
return Error ( "You need to provide your current password to make any changes" ) ;
}
var changingPass = ! string . IsNullOrEmpty ( ui . Password ) | | ! string . IsNullOrEmpty ( ui . ConfirmNewPassword ) ;
if ( changingPass )
{
if ( ! ui . Password . Equals ( ui ? . ConfirmNewPassword , StringComparison . CurrentCultureIgnoreCase ) )
{
return Error ( "Passwords do not match" ) ;
}
}
if ( ! EmailValidator . IsValidEmail ( ui . EmailAddress ) )
{
return Error ( $"The email address {ui.EmailAddress} is not a valid format" ) ;
}
// Get the user
var user = await UserManager . Users . FirstOrDefaultAsync ( x = > x . Id = = ui . Id ) ;
if ( user = = null )
{
return Error ( "The user does not exist" ) ;
}
// Make sure the pass is ok
UserManager . ClientIpAddress = GetRequestIP ( ) ;
var passwordCheck = await UserManager . CheckPasswordAsync ( user , ui . CurrentPassword ) ;
if ( ! passwordCheck )
{
return Error ( "Your password is incorrect" ) ;
}
user . Email = ui . EmailAddress ;
var updateResult = await UserManager . UpdateAsync ( user ) ;
if ( ! updateResult . Succeeded )
{
return new OmbiIdentityResult
{
Errors = updateResult . Errors . Select ( x = > x . Description ) . ToList ( )
} ;
}
if ( changingPass )
{
var result = await UserManager . ChangePasswordAsync ( user , ui . CurrentPassword , ui . Password ) ;
if ( ! result . Succeeded )
{
return new OmbiIdentityResult
{
Errors = result . Errors . Select ( x = > x . Description ) . ToList ( )
} ;
}
}
return new OmbiIdentityResult
{
Successful = true
} ;
}
/// <summary>
/// Updates the user.
/// </summary>
/// <param name = "ui" > The user.</param>
/// <returns></returns>
[HttpPut]
[PowerUser]
public async Task < OmbiIdentityResult > UpdateUser ( [ FromBody ] UserViewModel ui )
{
if ( ! EmailValidator . IsValidEmail ( ui . EmailAddress ) )
{
return Error ( $"The email address {ui.EmailAddress} is not a valid format" ) ;
}
if ( ! CanModifyUser ( ui . Claims . Select ( x = > x . Value ) ) )
{
return Error ( "You do not have the correct permissions to create this user" ) ;
}
// Get the user
var user = await UserManager . Users . FirstOrDefaultAsync ( x = > x . Id = = ui . Id ) ;
user . Alias = ui . Alias ;
user . Email = ui . EmailAddress ;
user . MovieRequestLimit = ui . MovieRequestLimit ;
user . EpisodeRequestLimit = ui . EpisodeRequestLimit ;
user . MusicRequestLimit = ui . MusicRequestLimit ;
user . EpisodeRequestLimitType = ui . EpisodeRequestLimitType ;
user . MusicRequestLimitType = ui . MusicRequestLimitType ;
user . MovieRequestLimitType = ui . MovieRequestLimitType ;
if ( ui . Password . HasValue ( ) )
{
user . PasswordHash = UserManager . PasswordHasher . HashPassword ( user , ui . Password ) ;
}
if ( ui . StreamingCountry . HasValue ( ) )
{
user . StreamingCountry = ui . StreamingCountry ;
}
var updateResult = await UserManager . UpdateAsync ( user ) ;
if ( ! updateResult . Succeeded )
{
return new OmbiIdentityResult
{
Errors = updateResult . Errors . Select ( x = > x . Description ) . ToList ( )
} ;
}
// Get the roles
var userRoles = await UserManager . GetRolesAsync ( user ) ;
// Am I modifying myself?
var modifyingSelf = user . UserName . Equals ( User . Identity . Name , StringComparison . CurrentCultureIgnoreCase ) ;
foreach ( var role in userRoles )
{
if ( modifyingSelf & & role . Equals ( OmbiRoles . Admin ) )
{
// We do not want to remove the admin role from yourself, this must be an accident
var claim = ui . Claims . FirstOrDefault ( x = > x . Value = = OmbiRoles . Admin & & x . Enabled ) ;
ui . Claims . Remove ( claim ) ;
continue ;
}
await UserManager . RemoveFromRoleAsync ( user , role ) ;
}
var result = await AddRoles ( ui . Claims , user ) ;
if ( result . Any ( x = > ! x . Succeeded ) )
{
var messages = new List < string > ( ) ;
foreach ( var errors in result . Where ( x = > ! x . Succeeded ) )
{
messages . AddRange ( errors . Errors . Select ( x = > x . Description ) . ToList ( ) ) ;
}
return new OmbiIdentityResult
{
Errors = messages
} ;
}
// Add the quality profiles
if ( ui . UserQualityProfiles ! = null )
{
var currentQualityProfiles = await
_userQualityProfiles . GetAll ( ) . FirstOrDefaultAsync ( x = > x . UserId = = user . Id ) ;
var add = false ;
if ( currentQualityProfiles = = null )
{
currentQualityProfiles = new UserQualityProfiles
{
UserId = user . Id
} ;
add = true ;
}
currentQualityProfiles . RadarrQualityProfile = ui . UserQualityProfiles . RadarrQualityProfile ;
currentQualityProfiles . RadarrRootPath = ui . UserQualityProfiles . RadarrRootPath ;
currentQualityProfiles . SonarrQualityProfile = ui . UserQualityProfiles . SonarrQualityProfile ;
currentQualityProfiles . SonarrQualityProfileAnime = ui . UserQualityProfiles . SonarrQualityProfileAnime ;
currentQualityProfiles . SonarrRootPath = ui . UserQualityProfiles . SonarrRootPath ;
currentQualityProfiles . SonarrRootPathAnime = ui . UserQualityProfiles . SonarrRootPathAnime ;
if ( add )
{
await _userQualityProfiles . Add ( currentQualityProfiles ) ;
}
await _userQualityProfiles . SaveChangesAsync ( ) ;
}
return new OmbiIdentityResult
{
Successful = true
} ;
}
/// <summary>
/// Deletes the user.
/// </summary>
/// <param name="userId">The user.</param>
/// <returns></returns>
[HttpDelete("{userId}")]
[PowerUser]
public async Task < OmbiIdentityResult > DeleteUser ( string userId )
{
var userToDelete = await UserManager . Users . FirstOrDefaultAsync ( x = > x . Id = = userId ) ;
if ( userToDelete ! = null )
{
// Can we delete this user?
var userRoles = await UserManager . GetRolesAsync ( userToDelete ) ;
if ( ! CanModifyUser ( userRoles ) )
{
return Error ( "You do not have the correct permissions to delete this user" ) ;
}
var result = await _deletionEngine . DeleteUser ( userToDelete ) ;
if ( result . Succeeded )
{
return new OmbiIdentityResult
{
Successful = true
} ;
}
return new OmbiIdentityResult
{
Errors = result . Errors . Select ( x = > x . Description ) . ToList ( )
} ;
}
return Error ( "Could not find user to delete." ) ;
}
/// <summary>
/// Gets all available claims in the system.
/// </summary>
/// <returns></returns>
[HttpGet("claims")]
[PowerUser]
public async Task < IEnumerable < ClaimCheckboxes > > GetAllClaims ( )
{
var claims = new List < ClaimCheckboxes > ( ) ;
// Add the missing claims
var allRoles = await RoleManager . Roles . ToListAsync ( ) ;
var missing = allRoles . Select ( x = > x . Name ) ;
foreach ( var role in missing )
{
claims . Add ( new ClaimCheckboxes
{
Value = role ,
Enabled = false
} ) ;
}
return claims ;
}
/// <summary>
/// Send out the email with the reset link
/// </summary>
/// <param name="email"></param>
/// <returns></returns>
[HttpPost("reset")]
[AllowAnonymous]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task < OmbiIdentityResult > SubmitResetPassword ( [ FromBody ] SubmitPasswordReset email )
{
// Check if account exists
var user = await UserManager . FindByEmailAsync ( email . Email ) ;
var defaultMessage = new OmbiIdentityResult
{
Successful = true ,
Errors = new List < string > { "If this account exists you should recieve a password reset link." }
} ;
if ( user = = null )
{
return defaultMessage ;
}
var customizationSettings = await CustomizationSettings . GetSettingsAsync ( ) ;
var appName = ( string . IsNullOrEmpty ( customizationSettings . ApplicationName )
? "Ombi"
: customizationSettings . ApplicationName ) ;
var emailSettings = await EmailSettings . GetSettingsAsync ( ) ;
var appUrl = customizationSettings . AddToUrl ( "/token?token=" ) ;
var url = ( string . IsNullOrEmpty ( appUrl )
? $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}/token?token="
: appUrl ) ;
if ( user . UserType = = UserType . PlexUser )
{
await EmailProvider . SendAdHoc ( new NotificationMessage
{
To = user . Email ,
Subject = $"{appName} Password Reset" ,
Message =
$"You recently made a request to reset your {appName} account. Please click the link below to complete the process.<br/><br/>" +
$"<a href=\" https : //www.plex.tv/sign-in/password-reset/\"> Reset </a>"
} , emailSettings ) ;
}
else if ( user . UserType = = UserType . EmbyUser & & user . IsEmbyConnect )
{
await EmailProvider . SendAdHoc ( new NotificationMessage
{
To = user . Email ,
Subject = $"{appName} Password Reset" ,
Message =
$"You recently made a request to reset your {appName} account.<br/><br/>" +
$"To reset your password you need to go to <a href=\" https : //emby.media/community/index.php\">Emby.Media</a> and then click on your Username > Edit Profile > Email and Password"
} , emailSettings ) ;
}
else
{
// We have the user
var token = await UserManager . GeneratePasswordResetTokenAsync ( user ) ;
var encodedToken = WebUtility . UrlEncode ( token ) ;
await EmailProvider . SendAdHoc ( new NotificationMessage
{
To = user . Email ,
Subject = $"{appName} Password Reset" ,
Message =
$"You recently made a request to reset your {appName} account. Please click the link below to complete the process.<br/><br/>" +
$"<a href=\" { url } { encodedToken } \ "> Reset </a>"
} , emailSettings ) ;
}
return defaultMessage ;
}
/// <summary>
/// Resets the password
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
[HttpPost("resetpassword")]
[AllowAnonymous]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task < OmbiIdentityResult > ResetPassword ( [ FromBody ] ResetPasswordToken token )
{
var user = await UserManager . FindByEmailAsync ( token . Email ) ;
if ( user = = null )
{
return new OmbiIdentityResult
{
Successful = false ,
Errors = new List < string > { "Please check you email." }
} ;
}
var validToken = WebUtility . UrlDecode ( token . Token ) ;
validToken = validToken . Replace ( " " , "+" ) ;
var tokenValid = await UserManager . ResetPasswordAsync ( user , validToken , token . Password ) ;
if ( tokenValid . Succeeded )
{
return new OmbiIdentityResult
{
Successful = true ,
} ;
}
return new OmbiIdentityResult
{
Errors = tokenValid . Errors . Select ( x = > x . Description ) . ToList ( )
} ;
}
[HttpPost("welcomeEmail")]
[PowerUser]
public async Task < IActionResult > SendWelcomeEmail ( [ FromBody ] UserViewModel user )
{
var ombiUser = new OmbiUser
{
Alias = user . Alias ,
Email = user . EmailAddress ,
UserName = user . UserName
} ;
await WelcomeEmail . SendEmail ( ombiUser ) ;
return Ok ( ) ;
}
[HttpGet("accesstoken")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task < string > GetUserAccessToken ( )
{
if ( ! User . Identity ? . Name . HasValue ( ) ? ? true )
{
return Guid . Empty . ToString ( "N" ) ;
}
var username = User . Identity . Name . ToUpper ( ) ;
var user = await UserManager . Users . FirstOrDefaultAsync ( x = > x . NormalizedUserName = = username ) ;
if ( user = = null )
{
return Guid . Empty . ToString ( "N" ) ;
}
if ( user . UserAccessToken . IsNullOrEmpty ( ) )
{
// Let's create an access token for this user
user . UserAccessToken = Guid . NewGuid ( ) . ToString ( "N" ) ;
var result = await UserManager . UpdateAsync ( user ) ;
if ( ! result . Succeeded )
{
LogErrors ( result ) ;
return Guid . Empty . ToString ( "N" ) ;
}
}
return user . UserAccessToken ;
}
[HttpGet("accesstoken/{userId}")]
[PowerUser]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task < string > GetUserAccessToken ( string userId )
{
var user = await UserManager . Users . FirstOrDefaultAsync ( x = > x . Id = = userId ) ;
if ( user = = null )
{
return Guid . Empty . ToString ( "N" ) ;
}
if ( user . UserAccessToken . IsNullOrEmpty ( ) )
{
// Let's create an access token for this user
user . UserAccessToken = Guid . NewGuid ( ) . ToString ( "N" ) ;
var result = await UserManager . UpdateAsync ( user ) ;
if ( ! result . Succeeded )
{
LogErrors ( result ) ;
return Guid . Empty . ToString ( "N" ) ;
}
}
return user . UserAccessToken ;
}
[HttpGet("notificationpreferences")]
public async Task < List < UserNotificationPreferences > > GetUserPreferences ( )
{
var username = User . Identity . Name . ToUpper ( ) ;
var user = await UserManager . Users . FirstOrDefaultAsync ( x = > x . NormalizedUserName = = username ) ;
return await GetPreferences ( user ) ;
}
[HttpGet("notificationpreferences/{userId}")]
public async Task < List < UserNotificationPreferences > > GetUserPreferences ( string userId )
{
var user = await UserManager . Users . FirstOrDefaultAsync ( x = > x . Id = = userId ) ;
return await GetPreferences ( user ) ;
}
private readonly List < NotificationAgent > _excludedAgents = new List < NotificationAgent >
{
NotificationAgent . Email ,
NotificationAgent . Mobile ,
NotificationAgent . Webhook
} ;
private async Task < List < UserNotificationPreferences > > GetPreferences ( OmbiUser user )
{
var userPreferences = await _userNotificationPreferences . GetAll ( ) . Where ( x = > x . UserId = = user . Id ) . ToListAsync ( ) ;
var agents = Enum . GetValues ( typeof ( NotificationAgent ) ) . Cast < NotificationAgent > ( ) . Where ( x = > ! _excludedAgents . Contains ( x ) ) ;
foreach ( var a in agents )
{
var agent = userPreferences . FirstOrDefault ( x = > x . Agent = = a ) ;
if ( agent = = null )
{
// Create the default
userPreferences . Add ( new UserNotificationPreferences
{
Agent = a ,
UserId = user . Id ,
} ) ;
}
}
return userPreferences ;
}
[HttpPost("NotificationPreferences")]
[ProducesResponseType(404)]
[ProducesResponseType(401)]
public async Task < IActionResult > AddUserNotificationPreference ( [ FromBody ] List < AddNotificationPreference > preferences )
{
foreach ( var pref in preferences )
{
// Make sure the user exists
var user = await UserManager . Users . FirstOrDefaultAsync ( x = > x . Id = = pref . UserId ) ;
if ( user = = null )
{
return NotFound ( ) ;
}
// Check if we are editing a different user than ourself, if we are then we need to power user role
var username = User . Identity . Name . ToUpper ( ) ;
var me = await UserManager . Users . FirstOrDefaultAsync ( x = > x . NormalizedUserName = = username ) ;
if ( ! me . Id . Equals ( user . Id , StringComparison . InvariantCultureIgnoreCase ) )
{
var isPowerUser = await UserManager . IsInRoleAsync ( me , OmbiRoles . PowerUser ) ;
var isAdmin = await UserManager . IsInRoleAsync ( me , OmbiRoles . Admin ) ;
if ( ! isPowerUser & & ! isAdmin )
{
return Unauthorized ( ) ;
}
}
// Make sure we don't already have a preference for this agent
var existingPreference = await _userNotificationPreferences . GetAll ( )
. FirstOrDefaultAsync ( x = > x . UserId = = user . Id & & x . Agent = = pref . Agent ) ;
if ( existingPreference ! = null )
{
// Update it
existingPreference . Value = pref . Value ;
await _userNotificationPreferences . SaveChangesAsync ( ) ;
}
else
{
await _userNotificationPreferences . Add ( new UserNotificationPreferences
{
Agent = pref . Agent ,
UserId = pref . UserId ,
Value = pref . Value
} ) ;
}
}
return Json ( true ) ;
}
[HttpGet("newsletter/unsubscribe/{userId}")]
[AllowAnonymous]
public async Task < IActionResult > UnsubscribeUser ( string userId )
{
// lookup user
var user = await UserManager . Users . FirstOrDefaultAsync ( x = > x . Id = = userId ) ;
if ( user = = null )
{
return Ok ( ) ;
}
await UserManager . RemoveFromRoleAsync ( user , OmbiRoles . ReceivesNewsletter ) ;
return Ok ( ) ;
}
private async Task < List < IdentityResult > > AddRoles ( IEnumerable < ClaimCheckboxes > roles , OmbiUser ombiUser )
{
var roleResult = new List < IdentityResult > ( ) ;
foreach ( var role in roles )
{
if ( role . Enabled )
{
roleResult . Add ( await UserManager . AddToRoleAsync ( ombiUser , role . Value ) ) ;
}
}
return roleResult ;
}
private OmbiIdentityResult Error ( string message )
{
return new OmbiIdentityResult
{
Errors = new List < string > { message }
} ;
}
}
}