Skip to content

Commit

Permalink
feat: Provide a flag for missing users on Plex Server (#4688) (#4778)
Browse files Browse the repository at this point in the history
[skip ci]
  • Loading branch information
xweskingx authored Oct 19, 2022
1 parent b01f6dd commit b4a14c2
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 11 deletions.
79 changes: 79 additions & 0 deletions src/Ombi.Schedule.Tests/PlexUserImporterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -324,5 +324,84 @@ public async Task Import_Update_Plex_User()

_mocker.Verify<OmbiUserManager>(x => x.UpdateAsync(It.Is<OmbiUser>(x => x.ProviderUserId == "PLEX_ID" && x.Email == "email" && x.UserName == "user")), Times.Once);
}


[Test]
public async Task Import_Cleanup_Missing_Plex_Users()
{
_mocker.Setup<ISettingsService<UserManagementSettings>, Task<UserManagementSettings>>(x => x.GetSettingsAsync())
.ReturnsAsync(new UserManagementSettings
{
ImportPlexAdmin = true,
ImportPlexUsers = true,
DefaultRoles = new List<string>
{
OmbiRoles.RequestMovie
},
CleanupPlexUsers = true,
});
_mocker.Setup<IPlexApi, Task<PlexFriends>>(x => x.GetUsers(It.IsAny<string>())).ReturnsAsync(new PlexFriends
{
User = new UserFriends[]
{
}
});
_mocker.Setup<IPlexApi, Task<PlexAccount>>(x => x.GetAccount(It.IsAny<string>())).ReturnsAsync(new PlexAccount
{
user = new User
{
email = "email",
authentication_token = "user_token",
title = "user_title",
username = "user_username",
id = "user_id",
}
});

_mocker.Setup<OmbiUserManager, Task<IdentityResult>>(x => x.CreateAsync(It.Is<OmbiUser>(x => x.UserName == "user_username" && x.Email == "email" && x.ProviderUserId == "user_id" && x.UserType == UserType.PlexUser)))
.ReturnsAsync(IdentityResult.Success);
_mocker.Setup<OmbiUserManager, Task<IdentityResult>>(x => x.AddToRoleAsync(It.Is<OmbiUser>(x => x.UserName == "user_username"), It.Is<string>(x => x == OmbiRoles.Admin)))
.ReturnsAsync(IdentityResult.Success);

await _subject.Execute(null);

_mocker.Verify<OmbiUserManager>(x => x.DeleteAsync(It.Is<OmbiUser>(x => x.ProviderUserId == "PLEX_ID" && x.Email == "dupe" && x.UserName == "plex")), Times.Once);
}

[Test]
public async Task Import_Cleanup_Missing_Plex_Admin()
{
_mocker.Setup<ISettingsService<UserManagementSettings>, Task<UserManagementSettings>>(x => x.GetSettingsAsync())
.ReturnsAsync(new UserManagementSettings
{
ImportPlexAdmin = true,
ImportPlexUsers = false,
DefaultRoles = new List<string>
{
OmbiRoles.RequestMovie
},
CleanupPlexUsers = true,
});
_mocker.Setup<IPlexApi, Task<PlexAccount>>(x => x.GetAccount(It.IsAny<string>())).ReturnsAsync(new PlexAccount
{
user = new User
{
email = "diff_email",
authentication_token = "user_token",
title = "user_title",
username = "diff_username",
id = "diff_user_id",
}
});

_mocker.Setup<OmbiUserManager, Task<IdentityResult>>(x => x.CreateAsync(It.Is<OmbiUser>(x => x.UserName == "diff_username" && x.Email == "diff_email" && x.ProviderUserId == "diff_user_id" && x.UserType == UserType.PlexUser)))
.ReturnsAsync(IdentityResult.Success);
_mocker.Setup<OmbiUserManager, Task<IdentityResult>>(x => x.AddToRoleAsync(It.Is<OmbiUser>(x => x.UserName == "diff_username"), It.Is<string>(x => x == OmbiRoles.Admin)))
.ReturnsAsync(IdentityResult.Success);

await _subject.Execute(null);

_mocker.Verify<OmbiUserManager>(x => x.DeleteAsync(It.Is<OmbiUser>(x => x.ProviderUserId == "PLEX_ID" && x.Email == "dupe" && x.UserName == "plex")), Times.Once);
}
}
}
51 changes: 41 additions & 10 deletions src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,30 +56,55 @@ public async Task Execute(IJobExecutionContext job)

await _notification.SendNotificationToAdmins("Plex User Importer Started");
var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.PlexUser).ToListAsync();
List<OmbiUser> newOrUpdatedUsers = new List<OmbiUser>();

foreach (var server in settings.Servers)
{
if (string.IsNullOrEmpty(server.PlexAuthToken))
{
continue;
}


if (userManagementSettings.ImportPlexAdmin)
{
await ImportAdmin(userManagementSettings, server, allUsers);
OmbiUser newOrUpdatedAdmin = await ImportAdmin(userManagementSettings, server, allUsers);
if (newOrUpdatedAdmin != null)
{
newOrUpdatedUsers.Add(newOrUpdatedAdmin);
}
}
if (userManagementSettings.ImportPlexUsers)
{
await ImportPlexUsers(userManagementSettings, allUsers, server);
newOrUpdatedUsers.AddRange(await ImportPlexUsers(userManagementSettings, allUsers, server));
}
}

if (userManagementSettings.CleanupPlexUsers)
{
// Refresh users from updates
allUsers = await _userManager.Users.Where(x => x.UserType == UserType.PlexUser)
.ToListAsync();
var missingUsers = allUsers
.Where(x => !newOrUpdatedUsers.Contains(x));
foreach (var ombiUser in missingUsers)
{
_log.LogInformation("Deleting user {0} not found in Plex Server.", ombiUser.UserName);
await _userManager.DeleteAsync(ombiUser);
}
}


await _notification.SendNotificationToAdmins("Plex User Importer Finished");
}

private async Task ImportPlexUsers(UserManagementSettings userManagementSettings, List<OmbiUser> allUsers, PlexServers server)
private async Task<List<OmbiUser>> ImportPlexUsers(UserManagementSettings userManagementSettings,
List<OmbiUser> allUsers, PlexServers server)
{
var users = await _api.GetUsers(server.PlexAuthToken);

List<OmbiUser> newOrUpdatedUsers = new List<OmbiUser>();

foreach (var plexUser in users.User)
{
// Check if we should import this user
Expand Down Expand Up @@ -129,29 +154,34 @@ private async Task ImportPlexUsers(UserManagementSettings userManagementSettings
{
continue;
}
// Get the new user object to avoid any concurrency failures
var dbUser =
await _userManager.Users.FirstOrDefaultAsync(x => x.UserName == newUser.UserName);
if (userManagementSettings.DefaultRoles.Any())
{
// Get the new user object to avoid any concurrency failures
var dbUser =
await _userManager.Users.FirstOrDefaultAsync(x => x.UserName == newUser.UserName);
foreach (var defaultRole in userManagementSettings.DefaultRoles)
{
await _userManager.AddToRoleAsync(dbUser, defaultRole);
}
}
newOrUpdatedUsers.Add(dbUser);
}
else
{
newOrUpdatedUsers.Add(existingPlexUser);
// Do we need to update this user?
existingPlexUser.Email = plexUser.Email;
existingPlexUser.UserName = plexUser.Username;

await _userManager.UpdateAsync(existingPlexUser);
}
}

return newOrUpdatedUsers;
}

private async Task ImportAdmin(UserManagementSettings settings, PlexServers server, List<OmbiUser> allUsers)
private async Task<OmbiUser> ImportAdmin(UserManagementSettings settings, PlexServers server,
List<OmbiUser> allUsers)
{
var plexAdmin = (await _api.GetAccount(server.PlexAuthToken)).user;

Expand All @@ -166,15 +196,15 @@ private async Task ImportAdmin(UserManagementSettings settings, PlexServers serv
adminUserFromDb.UserName = plexAdmin.username;
adminUserFromDb.ProviderUserId = plexAdmin.id;
await _userManager.UpdateAsync(adminUserFromDb);
return;
return adminUserFromDb;
}

// Ensure we don't have a user with the same username
var normalUsername = plexAdmin.username.ToUpperInvariant();
if (await _userManager.Users.AnyAsync(x => x.NormalizedUserName == normalUsername))
{
_log.LogWarning($"Cannot add user {plexAdmin.username} because their username is already in Ombi, skipping this user");
return;
return null;
}

var newUser = new OmbiUser
Expand All @@ -190,11 +220,12 @@ private async Task ImportAdmin(UserManagementSettings settings, PlexServers serv
var result = await _userManager.CreateAsync(newUser);
if (!LogResult(result))
{
return;
return null;
}

var roleResult = await _userManager.AddToRoleAsync(newUser, OmbiRoles.Admin);
LogResult(roleResult);
return newUser;
}

private bool LogResult(IdentityResult result)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public class UserManagementSettings : Settings
{
public bool ImportPlexAdmin { get; set; }
public bool ImportPlexUsers { get; set; }
public bool CleanupPlexUsers { get; set; }
public bool ImportEmbyUsers { get; set; }
public bool ImportJellyfinUsers { get; set; }
public int MovieRequestLimit { get; set; }
Expand Down
1 change: 1 addition & 0 deletions src/Ombi/ClientApp/src/app/interfaces/ISettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ export interface ICustomPage extends ISettings {

export interface IUserManagementSettings extends ISettings {
importPlexUsers: boolean;
cleanupPlexUsers: boolean;
importPlexAdmin: boolean;
importEmbyUsers: boolean;
importJellyfinUsers: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
<div class="form-group">
<mat-slide-toggle id="importAdmin" [(ngModel)]="settings.importPlexAdmin">Import Plex Admin</mat-slide-toggle>
</div>

<div *ngIf="settings.importPlexUsers || settings.importPlexAdmin">
<mat-slide-toggle id="cleanupPlexUsers" [(ngModel)]="settings.cleanupPlexUsers">
Cleanup Plex Users</mat-slide-toggle>
</div>
<div *ngIf="plexUsers">
<p>Plex Users excluded from Import</p>

Expand Down Expand Up @@ -121,7 +126,7 @@ <h3>Default Request Limits</h3>
</div>
<div><button type="button" [disabled]="!enableImportButton" (click)="runImporter()" class="mat-focus-indicator mat-stroked-button mat-raised-button mat-button-base">
Run Importer<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>
</div>
<div class="md-form-field" style="margin-top:1em;"></div>
</fieldset>
</div>

0 comments on commit b4a14c2

Please sign in to comment.