mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-15 01:32:55 -07:00
parent
1813b45fb3
commit
560072eba4
33 changed files with 279 additions and 91 deletions
|
@ -18,5 +18,6 @@ namespace Ombi.Api.Plex
|
||||||
Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, int ratingKey);
|
Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, int ratingKey);
|
||||||
Task<PlexContainer> GetAllEpisodes(string authToken, string host, string section, int start, int retCount);
|
Task<PlexContainer> GetAllEpisodes(string authToken, string host, string section, int start, int retCount);
|
||||||
Task<PlexFriends> GetUsers(string authToken);
|
Task<PlexFriends> GetUsers(string authToken);
|
||||||
|
Task<PlexAccount> GetAccount(string authToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,13 +2,21 @@
|
||||||
|
|
||||||
namespace Ombi.Api.Plex.Models
|
namespace Ombi.Api.Plex.Models
|
||||||
{
|
{
|
||||||
|
public class PlexAccount
|
||||||
|
{
|
||||||
|
public User user { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class User
|
public class User
|
||||||
{
|
{
|
||||||
|
public string id { get; set; }
|
||||||
public string email { get; set; }
|
public string email { get; set; }
|
||||||
public string uuid { get; set; }
|
public string uuid { get; set; }
|
||||||
public string joined_at { get; set; }
|
public string joined_at { get; set; }
|
||||||
public string username { get; set; }
|
public string username { get; set; }
|
||||||
public string title { get; set; }
|
public string title { get; set; }
|
||||||
|
public string thumb { get; set; }
|
||||||
|
public string hasPassword { get; set; }
|
||||||
public string authentication_token { get; set; }
|
public string authentication_token { get; set; }
|
||||||
public Subscription subscription { get; set; }
|
public Subscription subscription { get; set; }
|
||||||
public Roles roles { get; set; }
|
public Roles roles { get; set; }
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace Ombi.Api.Plex
|
||||||
|
|
||||||
private const string SignInUri = "https://plex.tv/users/sign_in.json";
|
private const string SignInUri = "https://plex.tv/users/sign_in.json";
|
||||||
private const string FriendsUri = "https://plex.tv/pms/friends/all";
|
private const string FriendsUri = "https://plex.tv/pms/friends/all";
|
||||||
private const string GetAccountUri = "https://plex.tv/users/account";
|
private const string GetAccountUri = "https://plex.tv/users/account.json";
|
||||||
private const string ServerUri = "https://plex.tv/pms/servers.xml";
|
private const string ServerUri = "https://plex.tv/pms/servers.xml";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -52,6 +52,13 @@ namespace Ombi.Api.Plex
|
||||||
return await Api.Request<PlexStatus>(request);
|
return await Api.Request<PlexStatus>(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<PlexAccount> GetAccount(string authToken)
|
||||||
|
{
|
||||||
|
var request = new Request(GetAccountUri, string.Empty, HttpMethod.Get);
|
||||||
|
AddHeaders(request, authToken);
|
||||||
|
return await Api.Request<PlexAccount>(request);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<PlexServer> GetServer(string authToken)
|
public async Task<PlexServer> GetServer(string authToken)
|
||||||
{
|
{
|
||||||
var request = new Request(ServerUri, string.Empty, HttpMethod.Get, ContentType.Xml);
|
var request = new Request(ServerUri, string.Empty, HttpMethod.Get, ContentType.Xml);
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Ombi.Core.Claims;
|
|
||||||
using Ombi.Core.Rule.Rules.Request;
|
using Ombi.Core.Rule.Rules.Request;
|
||||||
using Ombi.Store.Entities.Requests;
|
using Ombi.Store.Entities.Requests;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using Ombi.Helpers;
|
||||||
|
|
||||||
namespace Ombi.Core.Tests.Rule.Request
|
namespace Ombi.Core.Tests.Rule.Request
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,8 +2,8 @@ using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Ombi.Core.Claims;
|
|
||||||
using Ombi.Core.Rule.Rules;
|
using Ombi.Core.Rule.Rules;
|
||||||
|
using Ombi.Helpers;
|
||||||
using Ombi.Store.Entities.Requests;
|
using Ombi.Store.Entities.Requests;
|
||||||
|
|
||||||
namespace Ombi.Core.Tests.Rule.Request
|
namespace Ombi.Core.Tests.Rule.Request
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Ombi.Core.Claims;
|
|
||||||
using Ombi.Core.Models.Requests;
|
using Ombi.Core.Models.Requests;
|
||||||
using Ombi.Core.Rule.Interfaces;
|
using Ombi.Core.Rule.Interfaces;
|
||||||
|
using Ombi.Helpers;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
using Ombi.Store.Entities.Requests;
|
using Ombi.Store.Entities.Requests;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
using Ombi.Core.Claims;
|
using Ombi.Store.Entities;
|
||||||
using Ombi.Store.Entities;
|
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Ombi.Core.Rule.Interfaces;
|
using Ombi.Core.Rule.Interfaces;
|
||||||
|
using Ombi.Helpers;
|
||||||
using Ombi.Store.Entities.Requests;
|
using Ombi.Store.Entities.Requests;
|
||||||
|
|
||||||
namespace Ombi.Core.Rule.Rules
|
namespace Ombi.Core.Rule.Rules
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Ombi.Core.Claims;
|
|
||||||
using Ombi.Core.Rule.Interfaces;
|
using Ombi.Core.Rule.Interfaces;
|
||||||
|
using Ombi.Helpers;
|
||||||
using Ombi.Store.Entities.Requests;
|
using Ombi.Store.Entities.Requests;
|
||||||
|
|
||||||
namespace Ombi.Core.Rule.Rules.Specific
|
namespace Ombi.Core.Rule.Rules.Specific
|
||||||
|
|
|
@ -108,7 +108,7 @@ namespace Ombi.Core.Senders
|
||||||
// Get the root path from the rootfolder selected.
|
// Get the root path from the rootfolder selected.
|
||||||
// For some reason, if we haven't got one use the first root folder in Sonarr
|
// For some reason, if we haven't got one use the first root folder in Sonarr
|
||||||
// TODO make this overrideable via the UI
|
// TODO make this overrideable via the UI
|
||||||
var rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder ?? 0, s);
|
var rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder ?? int.Parse(s.RootPath), s);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Does the series actually exist?
|
// Does the series actually exist?
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ombi.Core.Claims
|
namespace Ombi.Helpers
|
||||||
{
|
{
|
||||||
public static class OmbiRoles
|
public static class OmbiRoles
|
||||||
{
|
{
|
|
@ -39,7 +39,7 @@ namespace Ombi.Notifications.Interfaces
|
||||||
|
|
||||||
public async Task NotifyAsync(NotificationOptions model)
|
public async Task NotifyAsync(NotificationOptions model)
|
||||||
{
|
{
|
||||||
var configuration = GetConfiguration();
|
var configuration = await GetConfiguration();
|
||||||
await NotifyAsync(model, configuration);
|
await NotifyAsync(model, configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,6 +100,12 @@ namespace Ombi.Notifications.Interfaces
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the TV or Movie Request
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="requestId"></param>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <returns></returns>
|
||||||
protected virtual async Task LoadRequest(int requestId, RequestType type)
|
protected virtual async Task LoadRequest(int requestId, RequestType type)
|
||||||
{
|
{
|
||||||
if (type == RequestType.Movie)
|
if (type == RequestType.Movie)
|
||||||
|
@ -112,12 +118,19 @@ namespace Ombi.Notifications.Interfaces
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private T GetConfiguration()
|
private async Task<T> GetConfiguration()
|
||||||
{
|
{
|
||||||
var settings = Settings.GetSettings();
|
var settings = await Settings.GetSettingsAsync();
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the correct template from the DB
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agent"></param>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <param name="model"></param>
|
||||||
|
/// <returns></returns>
|
||||||
protected virtual async Task<NotificationMessageContent> LoadTemplate(NotificationAgent agent, NotificationType type, NotificationOptions model)
|
protected virtual async Task<NotificationMessageContent> LoadTemplate(NotificationAgent agent, NotificationType type, NotificationOptions model)
|
||||||
{
|
{
|
||||||
var template = await TemplateRepository.GetTemplate(agent, type);
|
var template = await TemplateRepository.GetTemplate(agent, type);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
@ -44,6 +45,8 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.PlexUser).ToListAsync();
|
var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.PlexUser).ToListAsync();
|
||||||
foreach (var server in settings.Servers)
|
foreach (var server in settings.Servers)
|
||||||
{
|
{
|
||||||
|
@ -52,6 +55,8 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await ImportAdmin(userManagementSettings, server, allUsers);
|
||||||
|
|
||||||
var users = await _api.GetUsers(server.PlexAuthToken);
|
var users = await _api.GetUsers(server.PlexAuthToken);
|
||||||
|
|
||||||
foreach (var plexUser in users.User)
|
foreach (var plexUser in users.User)
|
||||||
|
@ -80,12 +85,8 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
};
|
};
|
||||||
_log.LogInformation("Creating Plex user {0}", newUser.UserName);
|
_log.LogInformation("Creating Plex user {0}", newUser.UserName);
|
||||||
var result = await _userManager.CreateAsync(newUser);
|
var result = await _userManager.CreateAsync(newUser);
|
||||||
if (!result.Succeeded)
|
if (!LogResult(result))
|
||||||
{
|
{
|
||||||
foreach (var identityError in result.Errors)
|
|
||||||
{
|
|
||||||
_log.LogError(LoggingEvents.PlexUserImporter, identityError.Description);
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (userManagementSettings.DefaultRoles.Any())
|
if (userManagementSettings.DefaultRoles.Any())
|
||||||
|
@ -107,5 +108,59 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ImportAdmin(UserManagementSettings settings, PlexServers server, List<OmbiUser> allUsers)
|
||||||
|
{
|
||||||
|
if (!settings.ImportPlexAdmin)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var plexAdmin = (await _api.GetAccount(server.PlexAuthToken)).user;
|
||||||
|
|
||||||
|
// Check if the admin is already in the DB
|
||||||
|
var adminUserFromDb = allUsers.FirstOrDefault(x =>
|
||||||
|
x.ProviderUserId.Equals(plexAdmin.id, StringComparison.CurrentCultureIgnoreCase));
|
||||||
|
|
||||||
|
if (adminUserFromDb != null)
|
||||||
|
{
|
||||||
|
// Let's update the user
|
||||||
|
adminUserFromDb.Email = plexAdmin.email;
|
||||||
|
adminUserFromDb.UserName = plexAdmin.username;
|
||||||
|
adminUserFromDb.ProviderUserId = plexAdmin.id;
|
||||||
|
await _userManager.UpdateAsync(adminUserFromDb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newUser = new OmbiUser
|
||||||
|
{
|
||||||
|
UserType = UserType.PlexUser,
|
||||||
|
UserName = plexAdmin.username ?? plexAdmin.id,
|
||||||
|
ProviderUserId = plexAdmin.id,
|
||||||
|
Email = plexAdmin.email ?? string.Empty,
|
||||||
|
Alias = string.Empty
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await _userManager.CreateAsync(newUser);
|
||||||
|
if (!LogResult(result))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var roleResult = await _userManager.AddToRoleAsync(newUser, OmbiRoles.Admin);
|
||||||
|
LogResult(roleResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool LogResult(IdentityResult result)
|
||||||
|
{
|
||||||
|
if (!result.Succeeded)
|
||||||
|
{
|
||||||
|
foreach (var identityError in result.Errors)
|
||||||
|
{
|
||||||
|
_log.LogError(LoggingEvents.PlexUserImporter, identityError.Description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.Succeeded;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,8 +13,6 @@ namespace Ombi.Core.Settings.Models.External
|
||||||
public class PlexServers : ExternalSettings
|
public class PlexServers : ExternalSettings
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public bool EnableEpisodeSearching { get; set; }
|
|
||||||
|
|
||||||
public string PlexAuthToken { get; set; }
|
public string PlexAuthToken { get; set; }
|
||||||
public string MachineIdentifier { get; set; }
|
public string MachineIdentifier { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ namespace Ombi.Settings.Settings.Models
|
||||||
{
|
{
|
||||||
public class UserManagementSettings : Settings
|
public class UserManagementSettings : Settings
|
||||||
{
|
{
|
||||||
|
public bool ImportPlexAdmin { get; set; }
|
||||||
public bool ImportPlexUsers { get; set; }
|
public bool ImportPlexUsers { get; set; }
|
||||||
public bool ImportEmbyUsers { get; set; }
|
public bool ImportEmbyUsers { get; set; }
|
||||||
public List<string> DefaultRoles { get; set; } = new List<string>();
|
public List<string> DefaultRoles { get; set; } = new List<string>();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Ombi.Core.Claims;
|
using Ombi.Helpers;
|
||||||
|
|
||||||
namespace Ombi.Attributes
|
namespace Ombi.Attributes
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<p-growl [value]="notificationService.messages" [life]="3000"></p-growl>
|
<p-growl [value]="notificationService.messages" [life]="3000"></p-growl>
|
||||||
|
<div *ngIf="user.name">
|
||||||
|
<div *ngIf="hasRole('Admin') || hasRole('PowerUser')" class="adminUser"></div>
|
||||||
|
</div>
|
||||||
<nav *ngIf="showNav" class="navbar navbar-default navbar-fixed-top">
|
<nav *ngIf="showNav" class="navbar navbar-default navbar-fixed-top">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="navbar-header">
|
<div class="navbar-header">
|
||||||
|
|
|
@ -122,6 +122,7 @@ export interface IAuthenticationSettings extends ISettings {
|
||||||
|
|
||||||
export interface IUserManagementSettings extends ISettings {
|
export interface IUserManagementSettings extends ISettings {
|
||||||
importPlexUsers: boolean;
|
importPlexUsers: boolean;
|
||||||
|
importPlexAdmin: boolean;
|
||||||
importEmbyUsers: boolean;
|
importEmbyUsers: boolean;
|
||||||
defaultRoles: string[];
|
defaultRoles: string[];
|
||||||
bannedPlexUserIds: string[];
|
bannedPlexUserIds: string[];
|
||||||
|
|
|
@ -14,6 +14,12 @@ export interface IUser {
|
||||||
checked: boolean;
|
checked: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ICreateWizardUser {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
usePlexAdminAccount: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export enum UserType {
|
export enum UserType {
|
||||||
LocalUser = 1,
|
LocalUser = 1,
|
||||||
PlexUser = 2,
|
PlexUser = 2,
|
||||||
|
|
|
@ -28,7 +28,7 @@ export class MovieSearchComponent implements OnInit {
|
||||||
private readonly translate: TranslateService, private sanitizer: DomSanitizer) {
|
private readonly translate: TranslateService, private sanitizer: DomSanitizer) {
|
||||||
|
|
||||||
this.searchChanged
|
this.searchChanged
|
||||||
.debounceTime(600) // Wait Xms afterthe last event before emitting last event
|
.debounceTime(600) // Wait Xms after the last event before emitting last event
|
||||||
.distinctUntilChanged() // only emit if value is different from previous value
|
.distinctUntilChanged() // only emit if value is different from previous value
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
this.searchText = x as string;
|
this.searchText = x as string;
|
||||||
|
@ -40,9 +40,9 @@ export class MovieSearchComponent implements OnInit {
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
this.movieResults = x;
|
this.movieResults = x;
|
||||||
this.searchApplied = true;
|
this.searchApplied = true;
|
||||||
// Now let's load some exta info including IMDBId
|
// Now let's load some extra info including IMDB Id
|
||||||
// This way the search is fast at displaying results.
|
// This way the search is fast at displaying results.
|
||||||
this.getExtaInfo();
|
this.getExtraInfo();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ export class MovieSearchComponent implements OnInit {
|
||||||
this.searchService.popularMovies()
|
this.searchService.popularMovies()
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
this.movieResults = x;
|
this.movieResults = x;
|
||||||
this.getExtaInfo();
|
this.getExtraInfo();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public nowPlayingMovies() {
|
public nowPlayingMovies() {
|
||||||
|
@ -111,7 +111,7 @@ export class MovieSearchComponent implements OnInit {
|
||||||
this.searchService.nowPlayingMovies()
|
this.searchService.nowPlayingMovies()
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
this.movieResults = x;
|
this.movieResults = x;
|
||||||
this.getExtaInfo();
|
this.getExtraInfo();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public topRatedMovies() {
|
public topRatedMovies() {
|
||||||
|
@ -119,19 +119,19 @@ export class MovieSearchComponent implements OnInit {
|
||||||
this.searchService.topRatedMovies()
|
this.searchService.topRatedMovies()
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
this.movieResults = x;
|
this.movieResults = x;
|
||||||
this.getExtaInfo();
|
this.getExtraInfo();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public upcomingMovies() {
|
public upcomingMovies() {
|
||||||
this.clearResults();
|
this.clearResults();
|
||||||
this.searchService.upcomignMovies()
|
this.searchService.upcomingMovies()
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
this.movieResults = x;
|
this.movieResults = x;
|
||||||
this.getExtaInfo();
|
this.getExtraInfo();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private getExtaInfo() {
|
private getExtraInfo() {
|
||||||
|
|
||||||
this.movieResults.forEach((val, index) => {
|
this.movieResults.forEach((val, index) => {
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ export class MovieSearchGridComponent implements OnInit {
|
||||||
}
|
}
|
||||||
public upcomingMovies() {
|
public upcomingMovies() {
|
||||||
this.clearResults();
|
this.clearResults();
|
||||||
this.searchService.upcomignMovies()
|
this.searchService.upcomingMovies()
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
this.movieResults = x;
|
this.movieResults = x;
|
||||||
this.getExtaInfo();
|
this.getExtaInfo();
|
||||||
|
@ -130,7 +130,7 @@ export class MovieSearchGridComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getExtaInfo() {
|
private getExtaInfo() {
|
||||||
this.movieResults.forEach((val, index) => {
|
this.movieResults.forEach((val) => {
|
||||||
this.searchService.getMovieInformation(val.id)
|
this.searchService.getMovieInformation(val.id)
|
||||||
.subscribe(m => this.updateItem(val, m));
|
.subscribe(m => this.updateItem(val, m));
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Http } from "@angular/http";
|
||||||
import { AuthHttp } from "angular2-jwt";
|
import { AuthHttp } from "angular2-jwt";
|
||||||
import { Observable } from "rxjs/Rx";
|
import { Observable } from "rxjs/Rx";
|
||||||
|
|
||||||
import { ICheckbox, IIdentityResult, IResetPasswordToken, IUpdateLocalUser, IUser } from "../interfaces";
|
import { ICheckbox, ICreateWizardUser, IIdentityResult, IResetPasswordToken, IUpdateLocalUser, IUser } from "../interfaces";
|
||||||
import { ServiceAuthHelpers } from "./service.helpers";
|
import { ServiceAuthHelpers } from "./service.helpers";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -12,8 +12,8 @@ export class IdentityService extends ServiceAuthHelpers {
|
||||||
constructor(http: AuthHttp, private regularHttp: Http, public platformLocation: PlatformLocation) {
|
constructor(http: AuthHttp, private regularHttp: Http, public platformLocation: PlatformLocation) {
|
||||||
super(http, "/api/v1/Identity/", platformLocation);
|
super(http, "/api/v1/Identity/", platformLocation);
|
||||||
}
|
}
|
||||||
public createWizardUser(username: string, password: string): Observable<boolean> {
|
public createWizardUser(user: ICreateWizardUser): Observable<boolean> {
|
||||||
return this.regularHttp.post(`${this.url}Wizard/`, JSON.stringify({ username, password }), { headers: this.headers }).map(this.extractData);
|
return this.regularHttp.post(`${this.url}Wizard/`, JSON.stringify(user), { headers: this.headers }).map(this.extractData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getUser(): Observable<IUser> {
|
public getUser(): Observable<IUser> {
|
||||||
|
|
|
@ -22,7 +22,7 @@ export class SearchService extends ServiceAuthHelpers {
|
||||||
public popularMovies(): Observable<ISearchMovieResult[]> {
|
public popularMovies(): Observable<ISearchMovieResult[]> {
|
||||||
return this.http.get(`${this.url}/Movie/Popular`).map(this.extractData);
|
return this.http.get(`${this.url}/Movie/Popular`).map(this.extractData);
|
||||||
}
|
}
|
||||||
public upcomignMovies(): Observable<ISearchMovieResult[]> {
|
public upcomingMovies(): Observable<ISearchMovieResult[]> {
|
||||||
return this.http.get(`${this.url}/Movie/upcoming`).map(this.extractData);
|
return this.http.get(`${this.url}/Movie/upcoming`).map(this.extractData);
|
||||||
}
|
}
|
||||||
public nowPlayingMovies(): Observable<ISearchMovieResult[]> {
|
public nowPlayingMovies(): Observable<ISearchMovieResult[]> {
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
<label for="importPlex">Import Plex Users</label>
|
<label for="importPlex">Import Plex Users</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<input type="checkbox" id="importAdmin" [(ngModel)]="settings.importPlexAdmin">
|
||||||
|
<label for="importAdmin">Import Plex Admin</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div *ngIf="plexUsers">
|
<div *ngIf="plexUsers">
|
||||||
<p>Plex Users exclude from Import</p>
|
<p>Plex Users exclude from Import</p>
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,6 @@
|
||||||
<h1>User Management</h1>
|
<h1>User Management</h1>
|
||||||
|
|
||||||
|
|
||||||
<!--Search-->
|
|
||||||
<div class="row">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="input-group">
|
|
||||||
<div class="input-group-addon left-radius">
|
|
||||||
<i class="fa fa-search"></i>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input type="text" class="form-control" id="search" placeholder="Search" [(ngModel)]="searchTerm">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<button type="button" class="btn btn-success-outline" [routerLink]="['/usermanagement/add']">Add User</button>
|
<button type="button" class="btn btn-success-outline" [routerLink]="['/usermanagement/add']">Add User</button>
|
||||||
<!-- Table -->
|
<!-- Table -->
|
||||||
|
|
|
@ -18,7 +18,7 @@ export class CreateAdminComponent {
|
||||||
private router: Router, private auth: AuthService, private settings: SettingsService) { }
|
private router: Router, private auth: AuthService, private settings: SettingsService) { }
|
||||||
|
|
||||||
public createUser() {
|
public createUser() {
|
||||||
this.identityService.createWizardUser(this.username, this.password).subscribe(x => {
|
this.identityService.createWizardUser({username: this.username, password: this.password, usePlexAdminAccount: false}).subscribe(x => {
|
||||||
if (x) {
|
if (x) {
|
||||||
// Log me in.
|
// Log me in.
|
||||||
this.auth.login({ username: this.username, password: this.password, rememberMe:false }).subscribe(c => {
|
this.auth.login({ username: this.username, password: this.password, rememberMe:false }).subscribe(c => {
|
||||||
|
|
|
@ -23,3 +23,5 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<p-confirmDialog></p-confirmDialog>
|
|
@ -1,8 +1,11 @@
|
||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
|
|
||||||
|
import { ConfirmationService } from "primeng/primeng";
|
||||||
|
|
||||||
import { PlexService } from "../../services";
|
import { PlexService } from "../../services";
|
||||||
import { NotificationService } from "../../services";
|
import { IdentityService, NotificationService, SettingsService } from "../../services";
|
||||||
|
import { AuthService } from "./../../auth/auth.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "./plex.component.html",
|
templateUrl: "./plex.component.html",
|
||||||
|
@ -12,7 +15,12 @@ export class PlexComponent {
|
||||||
public login: string;
|
public login: string;
|
||||||
public password: string;
|
public password: string;
|
||||||
|
|
||||||
constructor(private plexService: PlexService, private router: Router, private notificationService: NotificationService) { }
|
constructor(private plexService: PlexService, private router: Router,
|
||||||
|
private notificationService: NotificationService,
|
||||||
|
private confirmationService: ConfirmationService,
|
||||||
|
private identityService: IdentityService,
|
||||||
|
private settings: SettingsService,
|
||||||
|
private auth: AuthService) { }
|
||||||
|
|
||||||
public requestAuthToken() {
|
public requestAuthToken() {
|
||||||
this.plexService.logIn(this.login, this.password).subscribe(x => {
|
this.plexService.logIn(this.login, this.password).subscribe(x => {
|
||||||
|
@ -20,7 +28,46 @@ export class PlexComponent {
|
||||||
this.notificationService.error("Username or password was incorrect. Could not authenticate with Plex.");
|
this.notificationService.error("Username or password was incorrect. Could not authenticate with Plex.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.router.navigate(["Wizard/CreateAdmin"]);
|
this.confirmationService.confirm({
|
||||||
|
message: "Do you want your Plex user to be the main admin account on Ombi?",
|
||||||
|
header: "Use Plex Account",
|
||||||
|
icon: "fa fa-check",
|
||||||
|
accept: () => {
|
||||||
|
this.identityService.createWizardUser({
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
usePlexAdminAccount: true,
|
||||||
|
}).subscribe(x => {
|
||||||
|
if (x) {
|
||||||
|
this.auth.login({ username: this.login, password: this.password, rememberMe:false }).subscribe(c => {
|
||||||
|
localStorage.setItem("id_token", c.access_token);
|
||||||
|
|
||||||
|
// Mark that we have done the settings now
|
||||||
|
this.settings.getOmbi().subscribe(ombi => {
|
||||||
|
ombi.wizard = true;
|
||||||
|
|
||||||
|
this.settings.saveOmbi(ombi).subscribe(x => {
|
||||||
|
this.settings.getUserManagementSettings().subscribe(usr => {
|
||||||
|
|
||||||
|
usr.importPlexAdmin = true;
|
||||||
|
this.settings.saveUserManagementSettings(usr).subscribe(saved => {
|
||||||
|
this.router.navigate(["login"]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.notificationService.error("Could not get the Plex Admin Information");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
reject: () => {
|
||||||
|
this.router.navigate(["Wizard/CreateAdmin"]);
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ import { NgModule } from "@angular/core";
|
||||||
import { FormsModule } from "@angular/forms";
|
import { FormsModule } from "@angular/forms";
|
||||||
import { RouterModule, Routes } from "@angular/router";
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
|
|
||||||
|
import {ConfirmationService, ConfirmDialogModule } from "primeng/primeng";
|
||||||
|
|
||||||
import { CreateAdminComponent } from "./createadmin/createadmin.component";
|
import { CreateAdminComponent } from "./createadmin/createadmin.component";
|
||||||
import { EmbyComponent } from "./emby/emby.component";
|
import { EmbyComponent } from "./emby/emby.component";
|
||||||
import { MediaServerComponent } from "./mediaserver/mediaserver.component";
|
import { MediaServerComponent } from "./mediaserver/mediaserver.component";
|
||||||
|
@ -25,6 +27,7 @@ const routes: Routes = [
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
|
ConfirmDialogModule,
|
||||||
RouterModule.forChild(routes),
|
RouterModule.forChild(routes),
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -41,6 +44,7 @@ const routes: Routes = [
|
||||||
PlexService,
|
PlexService,
|
||||||
IdentityService,
|
IdentityService,
|
||||||
EmbyService,
|
EmbyService,
|
||||||
|
ConfirmationService,
|
||||||
],
|
],
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
48
src/Ombi/Controllers/External/PlexController.cs
vendored
48
src/Ombi/Controllers/External/PlexController.cs
vendored
|
@ -43,32 +43,30 @@ namespace Ombi.Controllers.External
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Do we already have settings?
|
||||||
|
_log.LogDebug("OK, signing into Plex");
|
||||||
|
var settings = await PlexSettings.GetSettingsAsync();
|
||||||
|
if (!settings.Servers?.Any() ?? false) return null;
|
||||||
|
|
||||||
|
_log.LogDebug("This is our first time, good to go!");
|
||||||
|
|
||||||
// Do we already have settings?
|
var result = await PlexApi.SignIn(request);
|
||||||
_log.LogDebug("OK, signing into Plex");
|
|
||||||
var settings = await PlexSettings.GetSettingsAsync();
|
|
||||||
if (!settings.Servers?.Any() ?? false) return null;
|
|
||||||
|
|
||||||
_log.LogDebug("This is our first time, good to go!");
|
_log.LogDebug("Attempting to sign in to Plex.Tv");
|
||||||
|
if (!string.IsNullOrEmpty(result.user?.authentication_token))
|
||||||
var result = await PlexApi.SignIn(request);
|
|
||||||
|
|
||||||
_log.LogDebug("Attempting to sign in to Plex.Tv");
|
|
||||||
if (!string.IsNullOrEmpty(result.user?.authentication_token))
|
|
||||||
{
|
|
||||||
_log.LogDebug("Sign in successful");
|
|
||||||
_log.LogDebug("Getting servers");
|
|
||||||
var server = await PlexApi.GetServer(result.user.authentication_token);
|
|
||||||
var servers = server.Server.FirstOrDefault();
|
|
||||||
if (servers == null)
|
|
||||||
{
|
{
|
||||||
_log.LogWarning("Looks like we can't find any Plex Servers");
|
_log.LogDebug("Sign in successful");
|
||||||
}
|
_log.LogDebug("Getting servers");
|
||||||
_log.LogDebug("Adding first server");
|
var server = await PlexApi.GetServer(result.user.authentication_token);
|
||||||
|
var servers = server.Server.FirstOrDefault();
|
||||||
|
if (servers == null)
|
||||||
|
{
|
||||||
|
_log.LogWarning("Looks like we can't find any Plex Servers");
|
||||||
|
}
|
||||||
|
_log.LogDebug("Adding first server");
|
||||||
|
|
||||||
settings.Enable = true;
|
settings.Enable = true;
|
||||||
settings.Servers = new List<PlexServers> {
|
settings.Servers = new List<PlexServers> {
|
||||||
new PlexServers
|
new PlexServers
|
||||||
{
|
{
|
||||||
PlexAuthToken = result.user.authentication_token,
|
PlexAuthToken = result.user.authentication_token,
|
||||||
|
@ -81,11 +79,11 @@ namespace Ombi.Controllers.External
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
await PlexSettings.SaveSettingsAsync(settings);
|
await PlexSettings.SaveSettingsAsync(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
_log.LogDebug("Finished");
|
_log.LogDebug("Finished");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,14 +12,15 @@ using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using Ombi.Api.Plex;
|
||||||
using Ombi.Attributes;
|
using Ombi.Attributes;
|
||||||
using Ombi.Config;
|
using Ombi.Config;
|
||||||
using Ombi.Core.Authentication;
|
using Ombi.Core.Authentication;
|
||||||
using Ombi.Core.Claims;
|
|
||||||
using Ombi.Core.Helpers;
|
using Ombi.Core.Helpers;
|
||||||
using Ombi.Core.Models.UI;
|
using Ombi.Core.Models.UI;
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
|
using Ombi.Core.Settings.Models.External;
|
||||||
|
using Ombi.Helpers;
|
||||||
using Ombi.Models;
|
using Ombi.Models;
|
||||||
using Ombi.Models.Identity;
|
using Ombi.Models.Identity;
|
||||||
using Ombi.Notifications;
|
using Ombi.Notifications;
|
||||||
|
@ -35,6 +36,7 @@ using OmbiIdentityResult = Ombi.Models.Identity.IdentityResult;
|
||||||
|
|
||||||
namespace Ombi.Controllers
|
namespace Ombi.Controllers
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Identity Controller, the API for everything Identity/User related
|
/// The Identity Controller, the API for everything Identity/User related
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -48,7 +50,9 @@ namespace Ombi.Controllers
|
||||||
IWelcomeEmail welcome,
|
IWelcomeEmail welcome,
|
||||||
IMovieRequestRepository m,
|
IMovieRequestRepository m,
|
||||||
ITvRequestRepository t,
|
ITvRequestRepository t,
|
||||||
ILogger<IdentityController> l)
|
ILogger<IdentityController> l,
|
||||||
|
IPlexApi plexApi,
|
||||||
|
ISettingsService<PlexSettings> settings)
|
||||||
{
|
{
|
||||||
UserManager = user;
|
UserManager = user;
|
||||||
Mapper = mapper;
|
Mapper = mapper;
|
||||||
|
@ -60,6 +64,8 @@ namespace Ombi.Controllers
|
||||||
MovieRepo = m;
|
MovieRepo = m;
|
||||||
TvRepo = t;
|
TvRepo = t;
|
||||||
_log = l;
|
_log = l;
|
||||||
|
_plexApi = plexApi;
|
||||||
|
_plexSettings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
private OmbiUserManager UserManager { get; }
|
private OmbiUserManager UserManager { get; }
|
||||||
|
@ -71,7 +77,9 @@ namespace Ombi.Controllers
|
||||||
private IWelcomeEmail WelcomeEmail { get; }
|
private IWelcomeEmail WelcomeEmail { get; }
|
||||||
private IMovieRequestRepository MovieRepo { get; }
|
private IMovieRequestRepository MovieRepo { get; }
|
||||||
private ITvRequestRepository TvRepo { get; }
|
private ITvRequestRepository TvRepo { get; }
|
||||||
private ILogger<IdentityController> _log;
|
private readonly ILogger<IdentityController> _log;
|
||||||
|
private readonly IPlexApi _plexApi;
|
||||||
|
private readonly ISettingsService<PlexSettings> _plexSettings;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is what the Wizard will call when creating the user for the very first time.
|
/// This is what the Wizard will call when creating the user for the very first time.
|
||||||
|
@ -85,7 +93,7 @@ namespace Ombi.Controllers
|
||||||
[HttpPost("Wizard")]
|
[HttpPost("Wizard")]
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public async Task<bool> CreateWizardUser([FromBody] UserAuthModel user)
|
public async Task<bool> CreateWizardUser([FromBody] CreateUserWizardModel user)
|
||||||
{
|
{
|
||||||
var users = UserManager.Users;
|
var users = UserManager.Users;
|
||||||
if (users.Any())
|
if (users.Any())
|
||||||
|
@ -94,13 +102,48 @@ namespace Ombi.Controllers
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 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
|
||||||
|
};
|
||||||
|
|
||||||
|
return await SaveWizardUser(user, adminUser);
|
||||||
|
}
|
||||||
|
|
||||||
var userToCreate = new OmbiUser
|
var userToCreate = new OmbiUser
|
||||||
{
|
{
|
||||||
UserName = user.Username,
|
UserName = user.Username,
|
||||||
UserType = UserType.LocalUser
|
UserType = UserType.LocalUser
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = await UserManager.CreateAsync(userToCreate, user.Password);
|
return await SaveWizardUser(user, userToCreate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> SaveWizardUser(CreateUserWizardModel user, OmbiUser userToCreate)
|
||||||
|
{
|
||||||
|
IdentityResult result;
|
||||||
|
// 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)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
_log.LogInformation("Created User {0}", userToCreate.UserName);
|
_log.LogInformation("Created User {0}", userToCreate.UserName);
|
||||||
|
|
|
@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using Ombi.Core.Authentication;
|
using Ombi.Core.Authentication;
|
||||||
using Ombi.Core.Claims;
|
using Ombi.Helpers;
|
||||||
using Ombi.Models;
|
using Ombi.Models;
|
||||||
using Ombi.Models.Identity;
|
using Ombi.Models.Identity;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
|
|
9
src/Ombi/Models/CreateUserWizardModel.cs
Normal file
9
src/Ombi/Models/CreateUserWizardModel.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Ombi.Models
|
||||||
|
{
|
||||||
|
public class CreateUserWizardModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
public bool UsePlexAdminAccount { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,5 +5,6 @@
|
||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
public bool RememberMe { get; set; }
|
public bool RememberMe { get; set; }
|
||||||
|
public bool UsePlexAdminAccount { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue