private UserManagementUsersViewModel MapPlexAdmin(PlexAccount plexInfo, PlexUsers dbUser, DateTime lastLoggedIn) { var newUser = false; if (dbUser == null) { newUser = true; dbUser = new PlexUsers(); } var features = (Features)dbUser?.Features; var permissions = (Permissions)dbUser?.Permissions; var m = new UserManagementUsersViewModel { Id = plexInfo.Id, PermissionsFormattedString = newUser ? "Processing..." : (permissions == 0 ? "None" : permissions.ToString()), FeaturesFormattedString = newUser ? "Processing..." : features.ToString(), Username = plexInfo.Username, Type = UserType.PlexUser, EmailAddress = plexInfo.Email, Alias = dbUser?.UserAlias ?? string.Empty, LastLoggedIn = lastLoggedIn, }; m.Permissions.AddRange(GetPermissions(permissions)); m.Features.AddRange(GetFeatures(features)); return(m); }
public void LoginWithOwnerUsernameAndPasswordSuccessfully() { var expectedSettings = new AuthenticationSettings { UserAuthentication = true, UsePassword = true, PlexAuthToken = "abc" }; var plexFriends = new PlexFriends { User = new[] { new UserFriends() } }; var plexAuth = new PlexAuthentication { user = new User { authentication_token = "abc", username = "******" } }; var account = new PlexAccount { Username = "******" }; AuthMock.Setup(x => x.GetSettings()).Returns(expectedSettings); PlexMock.Setup(x => x.GetUsers(It.IsAny <string>())).Returns(plexFriends); PlexMock.Setup(x => x.SignIn(It.IsAny <string>(), It.IsAny <string>())).Returns(plexAuth); PlexMock.Setup(x => x.GetAccount(It.IsAny <string>())).Returns(account); var bootstrapper = new ConfigurableBootstrapper(with => { with.Module <UserLoginModule>(); with.Dependency(AuthMock.Object); with.Dependency(PlexMock.Object); with.RootPathProvider <TestRootPathProvider>(); }); bootstrapper.WithSession(new Dictionary <string, object>()); var browser = new Browser(bootstrapper); var result = browser.Post("/userlogin", with => { with.HttpRequest(); with.Header("Accept", "application/json"); with.FormValue("Username", "jamie"); with.FormValue("Password", "abc"); }); Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode)); Assert.That(result.Context.Request.Session[SessionKeys.UsernameKey], Is.EqualTo("jamie")); var body = JsonConvert.DeserializeObject <JsonResponseModel>(result.Body.AsString()); Assert.That(body.Result, Is.EqualTo(true)); AuthMock.Verify(x => x.GetSettings(), Times.Once); PlexMock.Verify(x => x.SignIn(It.IsAny <string>(), It.IsAny <string>()), Times.Once); PlexMock.Verify(x => x.GetUsers(It.IsAny <string>()), Times.Never); }
public async Task <Result <PlexAccount> > UpdatePlexAccountAsync(PlexAccount plexAccount) { var result = await _mediator.Send(new UpdatePlexAccountCommand(plexAccount)); if (result.IsFailed) { string msg = "Failed to validate the PlexAccount that will be updated"; result.Errors.Add(new Error(msg)); return(result.ToResult().LogError()); } var plexAccountDb = await _mediator.Send(new GetPlexAccountByIdQuery(plexAccount.Id)); if (plexAccountDb.IsFailed) { return(plexAccountDb); } // Re-validate if the password changed if (plexAccountDb.Value != null && plexAccountDb.Value.Password != plexAccount.Password) { await SetupAccountAsync(plexAccountDb.Value); return(await GetPlexAccountAsync(plexAccountDb.Value.Id)); } return(plexAccountDb); }
public async Task CreatePlexAccountAndShouldBeStoredInDB() { // Arrange var accountService = Container.GetPlexAccountService; var credentials = Secrets.Account1; //Act var newAccount = new PlexAccount { DisplayName = "Test Account 1", Username = credentials.Username, Password = credentials.Password, }; // Act var result = await accountService.CreatePlexAccountAsync(newAccount); // Assert result.IsSuccess.Should().BeTrue(); result.Value.Should().NotBeNull(); result.Value.DisplayName.Should().Be(newAccount.DisplayName); result.Value.Username.Should().Be(newAccount.Username); result.Value.Password.Should().Be(newAccount.Password); result.Value.PlexAccountServers.Should().NotBeEmpty(); result.Value.AuthenticationToken.Should().NotBeEmpty(); }
/// <inheritdoc/> public async Task <Result <bool> > RefreshLibrariesAsync(PlexAccount plexAccount, PlexServer plexServer) { if (plexServer == null) { string msg = "plexServer was null"; Log.Warning(msg); return(Result.Fail(msg)); } Log.Debug($"Refreshing PlexLibraries for plexServer: {plexServer.Name}"); var authToken = await _plexAuthenticationService.GetPlexServerTokenAsync(plexServer.Id, plexAccount.Id); if (authToken.IsFailed) { return(Result.Fail(new Error("Failed to retrieve the server auth token"))); } var libraries = await _plexServiceApi.GetLibrarySectionsAsync(authToken.Value, plexServer.ServerUrl); if (!libraries.Any()) { string msg = $"plexLibraries returned for server {plexServer.Name} - {plexServer.ServerUrl} was empty"; Log.Warning(msg); return(Result.Fail(msg)); } return(await _mediator.Send(new AddOrUpdatePlexLibrariesCommand(plexAccount, plexServer, libraries))); }
/// <summary> /// Retrieves all <see cref="PlexServer"/>s accessible by this <see cref="PlexAccount"/> from the Database. /// </summary> /// <param name="plexAccount">The <see cref="PlexAccount"/> to check with.</param> /// <param name="refresh">Should the <see cref="PlexServer"/>s data be retrieved from the PlexApi.</param> /// <returns>The list of <see cref="PlexServer"/>s.</returns> public async Task <Result <List <PlexServer> > > GetServersAsync(PlexAccount plexAccount, bool refresh = false) { if (plexAccount == null) { Log.Warning("The plexAccount was null"); return(Result.Fail("The plexAccount was null")); } // Retrieve all servers var serverList = await _mediator.Send(new GetAllPlexServersByPlexAccountIdQuery(plexAccount.Id)); if (refresh || !serverList.Value.Any()) { if (!serverList.Value.Any()) { Log.Warning($"PlexAccount {plexAccount.Id} did not have any PlexServers assigned"); } var refreshSuccess = await RefreshPlexServersAsync(plexAccount); if (refreshSuccess.IsFailed) { return(refreshSuccess.ToResult()); } serverList = await _mediator.Send(new GetAllPlexServersByPlexAccountIdQuery(plexAccount.Id)); if (serverList.IsFailed) { return(serverList); } } return(serverList); }
public async Task <string> GetPlexApiTokenAsync(PlexAccount plexAccount) { if (plexAccount == null) { Log.Warning("The plexAccount was null"); return(string.Empty); } if (plexAccount.AuthenticationToken != string.Empty) { // TODO Make the token refresh limit configurable if ((plexAccount.ValidatedAt - DateTime.Now).TotalDays < 30) { Log.Information("Plex AuthToken was still valid, using from local DB."); return(plexAccount.AuthenticationToken); } Log.Information("Plex AuthToken has expired, refreshing Plex AuthToken now."); return(await _plexApiService.RefreshPlexAuthTokenAsync(plexAccount)); } Log.Error($"PlexAccount with Id: {plexAccount.Id} contained an empty AuthToken!"); return(string.Empty); }
public async Task CreateAccountAsync_ShouldReturnValidAccount_WhenAccountDoesNotExist() { // Arrange var accountService = Container.GetPlexAccountService; var newAccount = new PlexAccount("TestUsername", "Password123"); // Act var account = await accountService.CreatePlexAccountAsync(newAccount); // Assert account.Should().NotBeNull(); }
public async Task <string> RefreshPlexAuthTokenAsync(PlexAccount plexAccount) { var result = await PlexSignInAsync(plexAccount.Username, plexAccount.Password); if (result != null) { Log.Information($"Returned token was: {result.User.AuthenticationToken}"); return(result.User.AuthenticationToken); } Log.Error("Result from RequestPlexSignInDataAsync() was null."); return(string.Empty); }
public void LoginWithOwnerUsernameAndPasswordSuccessfully() { var expectedSettings = new AuthenticationSettings { UserAuthentication = true, UsePassword = true }; var plexFriends = new PlexFriends { User = new[] { new UserFriends() } }; var plexAuth = new PlexAuthentication { user = new User { authentication_token = "abc", username = "******" } }; var account = new PlexAccount { Username = "******" }; AuthMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(expectedSettings); PlexMock.Setup(x => x.GetUsers(It.IsAny <string>())).Returns(plexFriends); PlexMock.Setup(x => x.SignIn(It.IsAny <string>(), It.IsAny <string>())).Returns(plexAuth); PlexMock.Setup(x => x.GetAccount(It.IsAny <string>())).Returns(account); Bootstrapper.WithSession(new Dictionary <string, object>()); var browser = new Browser(Bootstrapper); var result = browser.Post("/userlogin", with => { with.HttpRequest(); with.Header("Accept", "application/json"); with.FormValue("Username", "jamie"); with.FormValue("Password", "abc"); }); Assert.That(HttpStatusCode.SeeOther, Is.EqualTo(result.StatusCode)); Assert.That(result.Headers.Contains(new KeyValuePair <string, string>("Location", "http://www.searchindex.com/"))); // Redirect header AuthMock.Verify(x => x.GetSettingsAsync(), Times.Once); PlexMock.Verify(x => x.SignIn(It.IsAny <string>(), It.IsAny <string>()), Times.Once); PlexMock.Verify(x => x.GetUsers(It.IsAny <string>()), Times.Never); }
public async Task UpdateAccountAsync_ShouldReturnValidAccount_WhenAccountWasUpdated() { // Arrange var accountService = Container.GetPlexAccountService; var newAccount = new PlexAccount("TestUsername", "Password123"); var updatedAccount = new PlexAccount("TestUsername", "123PassWrd123"); // Act var accountDB = (await accountService.CreatePlexAccountAsync(newAccount)).Value; updatedAccount.Id = accountDB.Id; accountDB = (await accountService.UpdatePlexAccountAsync(updatedAccount)).Value; // Assert accountDB.Should().NotBeNull(); accountDB.Password.Should().Be(updatedAccount.Password); }
public async Task CreatePlexAccountAndRequestLibrary_ShouldReturnValidLibrary() { // Arrange var accountService = Container.GetPlexAccountService; var plexLibraryService = Container.GetPlexLibraryService; var credentials = Secrets.Account2; // Act var newAccount = new PlexAccount { DisplayName = "Test Account 1", Username = credentials.Username, Password = credentials.Password, }; // Act var result = await accountService.CreatePlexAccountAsync(newAccount); result.IsFailed.Should().BeFalse(); // Retrieve account with included PlexServers and PlexLibraries result = await accountService.GetPlexAccountAsync(result.Value.Id); // This is very specific to the plex account used var library = GetLibraryFromPlexAccount(result.Value); library.Should().NotBeNull(); var plexLibrary = await plexLibraryService.GetPlexLibraryAsync(library.Id, result.Value.Id); // Assert result.IsSuccess.Should().BeTrue(); result.Value.Should().NotBeNull(); result.Value.DisplayName.Should().Be(newAccount.DisplayName); result.Value.Username.Should().Be(newAccount.Username); result.Value.Password.Should().Be(newAccount.Password); result.Value.PlexAccountServers.Should().NotBeEmpty(); result.Value.AuthenticationToken.Should().NotBeEmpty(); plexLibrary.Value.Movies.Should().NotBeEmpty(); plexLibrary.Value.PlexServer.Should().NotBeNull(); plexLibrary.Value.HasMedia.Should().BeTrue(); }
public async Task <Result <List <PlexServer> > > GetPlexServersAsync(PlexAccount plexAccount, bool refresh = false) { if (refresh) { await _plexServerService.RefreshPlexServersAsync(plexAccount); } var result = await _mediator.Send(new GetPlexServersByPlexAccountIdQuery(plexAccount.Id)); if (!result.IsFailed) { Log.Warning("Validation failed when attempting to retrieve the PlexServers by PlexAccount id"); return(result); } Log.Information($"Retrieved {result.Value.Count} PlexServers from the database" + (refresh ? ", after refresh from the plexApi" : string.Empty)); return(result); }
/// <summary> /// Retrieves the latest <see cref="PlexServer"/> data, and the corresponding <see cref="PlexLibrary"/>, from the PlexAPI and stores it in the Database. /// </summary> /// <param name="plexAccount">PlexAccount to use to retrieve the servers.</param> /// <returns>Is successful.</returns> public async Task <Result <bool> > RefreshPlexServersAsync(PlexAccount plexAccount) { if (plexAccount == null) { return(Result.Fail("plexAccount was null").LogWarning()); } Log.Debug($"Refreshing Plex servers for PlexAccount: {plexAccount.Id}"); var token = await _plexAuthenticationService.GetPlexApiTokenAsync(plexAccount); if (string.IsNullOrEmpty(token)) { Log.Warning("Token was empty"); return(Result.Fail("Token was empty")); } var serverList = await _plexServiceApi.GetServersAsync(token); if (!serverList.Any()) { return(Result.Ok(false)); } // Add initial entry for the plex servers var result = await _mediator.Send(new AddOrUpdatePlexServersCommand(plexAccount, serverList)); if (result.IsFailed) { return(result); } var plexServersResult = await InspectPlexServers(plexAccount, serverList); if (plexServersResult.IsFailed) { return(plexServersResult.ToResult()); } return(await _mediator.Send(new UpdatePlexServersCommand(plexServersResult.Value))); }
/// <summary> /// Sign into the Plex API /// This is for authenticating users credentials with Plex /// <para>NOTE: Plex "Managed" users do not work</para> /// </summary> /// <returns></returns> public async Task <User> SignIn(string username, string password) { var userRequest = new PlexUserRequest { User = new UserRequest { Login = username, Password = password } }; var apiRequest = new ApiRequestBuilder("https://plex.tv/users/sign_in.json", string.Empty, HttpMethod.Post) .AddRequestHeaders(GetClientIdentifierHeader()) .AddRequestHeaders(GetClientMetaHeaders()) .AcceptJson() .AddJsonBody(userRequest) .Build(); PlexAccount account = await ApiService.InvokeApiAsync <PlexAccount>(apiRequest); return(account?.User); }
public async Task UpdatePlexAccountWithNewDisplayName() { // Arrange var accountService = Container.GetPlexAccountService; var credentials = Secrets.Account1; //Act var newAccount = new PlexAccount { DisplayName = "Test Account", Username = credentials.Username, Password = credentials.Password, }; // Act var createResult = await accountService.CreatePlexAccountAsync(newAccount); createResult.Value.DisplayName = "Updated Test Account 999"; var updateResult = await accountService.UpdatePlexAccountAsync(createResult.Value); // Assert createResult.IsSuccess.Should().BeTrue(); createResult.Value.Should().NotBeNull(); createResult.Value.Username.Should().Be(newAccount.Username); createResult.Value.Password.Should().Be(newAccount.Password); createResult.Value.PlexAccountServers.Should().NotBeEmpty(); createResult.Value.AuthenticationToken.Should().NotBeEmpty(); updateResult.IsSuccess.Should().BeTrue(); updateResult.Value.Should().NotBeNull(); updateResult.Value.DisplayName.Should().Be("Updated Test Account 999"); updateResult.Value.Username.Should().Be(createResult.Value.Username); updateResult.Value.Password.Should().Be(createResult.Value.Password); updateResult.Value.PlexAccountServers.Should().NotBeEmpty(); updateResult.Value.AuthenticationToken.Should().NotBeEmpty(); }
public AddOrUpdatePlexServersCommand(PlexAccount plexAccount, List <PlexServer> plexServers) { PlexAccount = plexAccount; PlexServers = plexServers; }
/// <summary> /// Creates an <see cref="PlexAccount"/> in the Database and performs an SetupAccountAsync(). /// </summary> /// <param name="plexAccount">The unique account.</param> /// <returns>Returns the added account after setup.</returns> public async Task <Result <PlexAccount> > CreatePlexAccountAsync(PlexAccount plexAccount) { Log.Debug($"Creating account with username {plexAccount.Username}"); var result = await CheckIfUsernameIsAvailableAsync(plexAccount.Username); // Fail on validation errors if (result.IsFailed) { return(result.ToResult <PlexAccount>()); } if (result.Value == false) { string msg = $"Account with username {plexAccount.Username} cannot be created due to an account with the same username already existing"; Log.Warning(msg); return(result.ToResult().WithError(msg)); } // Check account for validity var checkAccount = await ValidatePlexAccountAsync(plexAccount.Username, plexAccount.Password); if (checkAccount.IsFailed || !checkAccount.Value) { return(checkAccount.ToResult <PlexAccount>()); } // Create PlexAccount var createResult = await _mediator.Send(new CreatePlexAccountCommand(plexAccount)); if (createResult.IsFailed) { string msg = "Failed to validate the PlexAccount that will be created"; Log.Warning(msg); createResult.Errors.Add(new Error(msg)); return(createResult.ToResult()); } // Setup PlexAccount var accountInDb = await _mediator.Send(new GetPlexAccountByIdQuery(createResult.Value, true, true)); if (accountInDb.IsFailed) { return(accountInDb); } var isSuccessful = await SetupAccountAsync(accountInDb.Value); if (isSuccessful.IsFailed) { return(isSuccessful.ToResult()); } if (isSuccessful.Value) { return(await _mediator.Send(new GetPlexAccountByIdQuery(accountInDb.Value.Id, true, true))); } // Failed to setup account successfully, return errors return(isSuccessful.ToResult <PlexAccount>()); }
public CreatePlexAccountCommand(PlexAccount plexAccount) { PlexAccount = plexAccount; }
public Task <string> GetPlexTokenAsync(PlexAccount plexAccount) { return(_plexAuthenticationService.GetPlexApiTokenAsync(plexAccount)); }
public AddOrUpdatePlexLibrariesCommand(PlexAccount plexAccount, PlexServer plexServer, List <PlexLibrary> plexLibraries) { PlexAccount = plexAccount; PlexServer = plexServer; PlexLibraries = plexLibraries; }
/// <summary> /// This retrieves all the <see cref="PlexAccount"/> related data from the PlexApi. It's assumed that the <see cref="PlexAccount"/> has already been created in the Database. /// </summary> /// <param name="plexAccount">The <see cref="PlexAccount"/> to setup.</param> /// <returns>If the setup was successful.</returns> private async Task <Result <bool> > SetupAccountAsync(PlexAccount plexAccount) { if (plexAccount == null) { string msg = "plexAccount was null"; Log.Warning(msg); return(Result.Fail(msg)); } if (plexAccount.Id <= 0) { string msg = $"plexAccount.id was {plexAccount.Id}"; Log.Warning(msg); return(Result.Fail(msg)); } Log.Debug("Setting up new PlexAccount"); await _signalRService.SendPlexAccountRefreshUpdate(plexAccount.Id, 0, 1); // Request new PlexAccount var plexAccountFromApi = await _plexApiService.PlexSignInAsync(plexAccount.Username, plexAccount.Password); if (plexAccountFromApi == null) { string msg = $"The plexAccount with {plexAccount.Username} was invalid when sent to the PlexApi"; Log.Warning(msg); return(Result.Fail(msg)); } // Merge the plexAccount from the Api into the current one. plexAccount.FromPlexApi(plexAccountFromApi); var result = await _mediator.Send(new UpdatePlexAccountCommand(plexAccount)); if (result.IsFailed) { string msg = "Failed to validate the plexAccount before the update process in the database"; Log.Warning(msg); return(result.ToResult().WithError(msg)); } // Retrieve and store servers var refreshResult = await _plexServerService.RefreshPlexServersAsync(plexAccount); if (refreshResult.IsFailed) { string msg = "Failed to refresh the PlexServers when setting up the PlexAccount"; Log.Warning(msg); return(refreshResult.ToResult().WithError(msg)); } var plexServerList = await _mediator.Send(new GetPlexServersByPlexAccountIdQuery(plexAccount.Id)); if (plexServerList.IsFailed) { return(plexServerList.ToResult()); } // Retrieve and store the corresponding PlexLibraries if (!plexServerList.Value.Any()) { Log.Debug("Account was setup successfully, but did not have access to any servers!"); return(Result.Ok(false)); } Log.Debug("Account was setup successfully!"); return(Result.Ok(true)); }
/// /// <summary> /// Check if this account is valid by querying the Plex API. /// </summary> /// <param name="plexAccount">The account to check.</param> /// <returns>Are the account credentials valid.</returns> public Task <Result <bool> > ValidatePlexAccountAsync(PlexAccount plexAccount) { return(ValidatePlexAccountAsync(plexAccount.Username, plexAccount.Password)); }
private PlexLibrary GetLibraryFromPlexAccount(PlexAccount plexAccount) { // Retrieve the movies library return(plexAccount.PlexServers[1]?.PlexLibraries?.Find(x => x.Title == "Movies") ?? null); }
private async Task <Result <List <PlexServer> > > InspectPlexServers(PlexAccount plexAccount, List <PlexServer> plexServers) { // The servers have an OwnerId of 0 when it belongs to the PlexAccount that was used to request it. plexServers.ForEach(plexServer => { if (plexServer.OwnerId == 0) { plexServer.OwnerId = plexAccount.PlexId; } if (plexServer.Port == 443 && plexServer.Scheme == "http") { plexServer.Scheme = "https"; } }); await _signalRService.SendPlexAccountRefreshUpdate(plexAccount.Id, 0, plexServers.Count); int finishedCount = 0; async Task SendProgress() { Interlocked.Increment(ref finishedCount); // Send progress update to clients await _signalRService.SendPlexAccountRefreshUpdate(plexAccount.Id, finishedCount, plexServers.Count); } var tasks = plexServers.Select(async plexServer => { var serverStatusResult = await CheckPlexServerStatusAsync(plexServer, plexAccount.Id, false); if (serverStatusResult.IsFailed) { Log.Error($"Failed to retrieve the serverStatus for {plexServer.Name} - {plexServer.ServerUrl}"); serverStatusResult.LogError(); return; } // Apply possible fixes and try again if (!serverStatusResult.Value.IsSuccessful) { Log.Information($"Attempting to DNS fix the connection with server {plexServer.Name}"); plexServer.ServerFixApplyDNSFix = true; serverStatusResult = await CheckPlexServerStatusAsync(plexServer, plexAccount.Id, false); if (!serverStatusResult.Value.IsSuccessful) { plexServer.ServerFixApplyDNSFix = false; await SendProgress(); Log.Error($"ServerFixApplyDNSFix did not help with server {plexServer.Name} - {plexServer.ServerUrl}"); return; } Log.Information($"ServerFixApplyDNSFix worked on {plexServer.Name}, connection successful!"); } await _plexLibraryService.RefreshLibrariesAsync(plexAccount, plexServer); await SendProgress(); }); await Task.WhenAll(tasks); return(Result.Ok(plexServers)); }
public Task <string> RefreshPlexAuthTokenAsync(PlexAccount account) { return(_plexApi.RefreshPlexAuthTokenAsync(account)); }
public UpdatePlexAccountCommand(PlexAccount plexAccount) { PlexAccount = plexAccount; }