Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide a flag for missing users on Plex Server (#4688) #4778

Merged
merged 1 commit into from
Oct 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>