mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-19 21:03:17 -07:00
stuff #865 need to work on the claims correctly
This commit is contained in:
parent
224b942190
commit
b4789363e8
14 changed files with 169 additions and 87 deletions
|
@ -88,6 +88,7 @@ namespace Ombi.Core.IdentityResolver
|
||||||
|
|
||||||
public async Task<UserDto> UpdateUser(UserDto userDto)
|
public async Task<UserDto> UpdateUser(UserDto userDto)
|
||||||
{
|
{
|
||||||
|
userDto.Claims.RemoveAll(x => x.Type == ClaimTypes.Country); // This is a hack around the Mapping Profile
|
||||||
var user = Mapper.Map<User>(userDto);
|
var user = Mapper.Map<User>(userDto);
|
||||||
return Mapper.Map<UserDto>(await UserRepository.UpdateUser(user));
|
return Mapper.Map<UserDto>(await UserRepository.UpdateUser(user));
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ namespace Ombi.Store.Context
|
||||||
DbSet<PlexContent> PlexContent { get; set; }
|
DbSet<PlexContent> PlexContent { get; set; }
|
||||||
DbSet<User> Users { get; set; }
|
DbSet<User> Users { get; set; }
|
||||||
EntityEntry<GlobalSettings> Entry(GlobalSettings settings);
|
EntityEntry<GlobalSettings> Entry(GlobalSettings settings);
|
||||||
|
EntityEntry<User> Entry(User settings);
|
||||||
EntityEntry<TEntity> Attach<TEntity>(TEntity entity) where TEntity : class;
|
EntityEntry<TEntity> Attach<TEntity>(TEntity entity) where TEntity : class;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using System.IO;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Resources;
|
using System.Resources;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||||
|
@ -38,7 +39,12 @@ namespace Ombi.Store.Context
|
||||||
|
|
||||||
public EntityEntry<GlobalSettings> Entry(GlobalSettings settings)
|
public EntityEntry<GlobalSettings> Entry(GlobalSettings settings)
|
||||||
{
|
{
|
||||||
return Entry(settings);
|
return base.Entry(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityEntry<User> Entry(User settings)
|
||||||
|
{
|
||||||
|
return base.Entry(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
|
|
|
@ -67,7 +67,9 @@ namespace Ombi.Store.Repository
|
||||||
|
|
||||||
public async Task<User> UpdateUser(User user)
|
public async Task<User> UpdateUser(User user)
|
||||||
{
|
{
|
||||||
Db.Users.Update(user);
|
Db.Entry(user).State = EntityState.Modified;
|
||||||
|
Db.Entry(user).Property(x => x.Salt).IsModified = false;
|
||||||
|
Db.Entry(user).Property(x => x.Password).IsModified = false;
|
||||||
await Db.SaveChangesAsync();
|
await Db.SaveChangesAsync();
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
10
src/Ombi/wwwroot/app/app.component.css
Normal file
10
src/Ombi/wwwroot/app/app.component.css
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
@media (max-width: 978px) {
|
||||||
|
.top-spacing {
|
||||||
|
padding-top: 10%
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (min-width: 979px) {
|
||||||
|
.top-spacing {
|
||||||
|
padding-top: 5%
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,15 +24,15 @@
|
||||||
<ul class="nav navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/requests']"><i class="fa fa-plus"></i> Requests</a></li>
|
<li [routerLinkActive]="['active']"><a [routerLink]="['/requests']"><i class="fa fa-plus"></i> Requests</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul *ngIf="isAdmin || isPowerUser" class="nav navbar-nav">
|
<ul *ngIf="hasRole('Admin') || hasRole('PowerUser')" class="nav navbar-nav">
|
||||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/usermanagement']"><i class="fa fa-user"></i> User Management</a></li>
|
<li [routerLinkActive]="['active']"><a [routerLink]="['/usermanagement']"><i class="fa fa-user"></i> User Management</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
<li *ngIf="isAdmin" [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Ombi']"><i class="fa fa-cog"></i> Settings</a></li>
|
<li *ngIf="hasRole('Admin') " [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Ombi']"><i class="fa fa-cog"></i> Settings</a></li>
|
||||||
<li [routerLinkActive]="['active']" class="dropdown">
|
<li [routerLinkActive]="['active']" class="dropdown">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><i class="fa fa-user"></i> Welcome {{username}} <span class="caret"></span></a>
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><i class="fa fa-user"></i> Welcome {{user.name}} <span class="caret"></span></a>
|
||||||
<ul class="dropdown-menu" role="menu">
|
<ul class="dropdown-menu" role="menu">
|
||||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/user/changepassword']"><i class="fa fa-key"></i> Change Password</a></li>
|
<li [routerLinkActive]="['active']"><a [routerLink]="['/user/changepassword']"><i class="fa fa-key"></i> Change Password</a></li>
|
||||||
<li [routerLinkActive]="['active']"><a (click)="logOut()"><i class="fa fa-sign-out"></i> Logout</a></li>
|
<li [routerLinkActive]="['active']"><a (click)="logOut()"><i class="fa fa-sign-out"></i> Logout</a></li>
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
<div class="container" style="padding-top: 5%">
|
<div class="container top-spacing">
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -3,45 +3,50 @@ import { Router } from '@angular/router';
|
||||||
import { NotificationService } from './services/notification.service';
|
import { NotificationService } from './services/notification.service';
|
||||||
import { SettingsService } from './services/settings.service';
|
import { SettingsService } from './services/settings.service';
|
||||||
import { AuthService } from './auth/auth.service';
|
import { AuthService } from './auth/auth.service';
|
||||||
import { IdentityService } from './services/identity.service';
|
import { ILocalUser } from './auth/IUserLogin';
|
||||||
|
|
||||||
import { ICustomizationSettings } from './interfaces/ISettings';
|
import { ICustomizationSettings } from './interfaces/ISettings';
|
||||||
|
|
||||||
|
import style from './app.component.css';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ombi',
|
selector: 'ombi',
|
||||||
moduleId: module.id,
|
moduleId: module.id,
|
||||||
templateUrl: './app.component.html'
|
templateUrl: './app.component.html',
|
||||||
|
styles: [style]
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnInit {
|
export class AppComponent implements OnInit {
|
||||||
|
|
||||||
constructor(public notificationService: NotificationService, public authService: AuthService, private router: Router, private settingsService: SettingsService
|
constructor(public notificationService: NotificationService, public authService: AuthService, private router: Router, private settingsService: SettingsService
|
||||||
, private identityService: IdentityService) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
customizationSettings: ICustomizationSettings;
|
customizationSettings: ICustomizationSettings;
|
||||||
|
user: ILocalUser;
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
|
||||||
|
this.user = this.authService.claims();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.settingsService.getCustomization().subscribe(x => this.customizationSettings = x);
|
this.settingsService.getCustomization().subscribe(x => this.customizationSettings = x);
|
||||||
|
|
||||||
this.router.events.subscribe(() => {
|
this.router.events.subscribe(() => {
|
||||||
this.username = localStorage.getItem('currentUser');
|
|
||||||
this.showNav = this.authService.loggedIn();
|
this.showNav = this.authService.loggedIn();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.isAdmin = this.identityService.hasRole("Admin");
|
|
||||||
this.isPowerUser = this.identityService.hasRole("PowerUser");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasRole(role: string): boolean {
|
||||||
|
return this.user.roles.some(r => r === role)
|
||||||
|
}
|
||||||
|
|
||||||
logOut() {
|
logOut() {
|
||||||
this.authService.logout();
|
this.authService.logout();
|
||||||
this.router.navigate(["login"]);
|
this.router.navigate(["login"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
username:string;
|
|
||||||
showNav: boolean;
|
showNav: boolean;
|
||||||
isAdmin: boolean;
|
|
||||||
isPowerUser:boolean;
|
|
||||||
}
|
}
|
|
@ -1,4 +1,9 @@
|
||||||
export interface IUserLogin {
|
export interface IUserLogin {
|
||||||
username: string,
|
username: string,
|
||||||
password:string
|
password:string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ILocalUser {
|
||||||
|
roles: string[],
|
||||||
|
name: string
|
||||||
}
|
}
|
|
@ -4,9 +4,9 @@ import { Observable } from 'rxjs/Rx';
|
||||||
|
|
||||||
import { ServiceHelpers } from '../services/service.helpers';
|
import { ServiceHelpers } from '../services/service.helpers';
|
||||||
|
|
||||||
import { IUserLogin } from './IUserLogin';
|
import { IUserLogin, ILocalUser } from './IUserLogin';
|
||||||
|
|
||||||
import { tokenNotExpired } from 'angular2-jwt';
|
import { tokenNotExpired, JwtHelper } from 'angular2-jwt';
|
||||||
|
|
||||||
import { Http } from '@angular/http';
|
import { Http } from '@angular/http';
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@ export class AuthService extends ServiceHelpers {
|
||||||
super(http, '/api/v1/token');
|
super(http, '/api/v1/token');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jwtHelper: JwtHelper = new JwtHelper();
|
||||||
|
|
||||||
login(login:IUserLogin) : Observable<any> {
|
login(login:IUserLogin) : Observable<any> {
|
||||||
return this.http.post(`${this.url}/`, JSON.stringify(login), { headers: this.headers })
|
return this.http.post(`${this.url}/`, JSON.stringify(login), { headers: this.headers })
|
||||||
.map(this.extractData);
|
.map(this.extractData);
|
||||||
|
@ -26,6 +28,29 @@ export class AuthService extends ServiceHelpers {
|
||||||
return tokenNotExpired('id_token');
|
return tokenNotExpired('id_token');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
claims(): ILocalUser {
|
||||||
|
if (this.loggedIn()) {
|
||||||
|
var token = localStorage.getItem('id_token');
|
||||||
|
|
||||||
|
var json = this.jwtHelper.decodeToken(token);
|
||||||
|
var roles = json["http://schemas.microsoft.com/ws/2008/06/identity/claims/role"]
|
||||||
|
var name = json["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"];
|
||||||
|
|
||||||
|
|
||||||
|
var u = { name: name, roles: [] as string[] };
|
||||||
|
if (roles instanceof Array) {
|
||||||
|
|
||||||
|
u.roles.concat(roles);
|
||||||
|
} else {
|
||||||
|
u.roles.push(roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <ILocalUser>u;
|
||||||
|
|
||||||
|
}
|
||||||
|
return <ILocalUser>{};
|
||||||
|
}
|
||||||
|
|
||||||
logout() {
|
logout() {
|
||||||
localStorage.removeItem('id_token');
|
localStorage.removeItem('id_token');
|
||||||
localStorage.removeItem('currentUser');
|
localStorage.removeItem('currentUser');
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
claims: ICheckbox[],
|
claims: ICheckbox[],
|
||||||
emailAddress: string,
|
emailAddress: string,
|
||||||
password: string,
|
password: string,
|
||||||
userType : UserType,
|
userType: UserType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,5 +18,6 @@ export enum UserType {
|
||||||
|
|
||||||
export interface ICheckbox {
|
export interface ICheckbox {
|
||||||
value: string,
|
value: string,
|
||||||
enabled:boolean,
|
enabled: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
13
src/Ombi/wwwroot/app/search/seriesinformation.component.css
Normal file
13
src/Ombi/wwwroot/app/search/seriesinformation.component.css
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#requestFloatingBtn {
|
||||||
|
position: fixed; /* Fixed/sticky position */
|
||||||
|
bottom: 20px; /* Place the button at the bottom of the page */
|
||||||
|
right: 30px; /* Place the button 30px from the right */
|
||||||
|
z-index: 99; /* Make sure it does not overlap */
|
||||||
|
cursor: pointer; /* Add a mouse pointer on hover */
|
||||||
|
padding: 15px; /* Some padding */
|
||||||
|
border-radius: 10px; /* Rounded corners */
|
||||||
|
}
|
||||||
|
|
||||||
|
#requestFloatingBtn:hover {
|
||||||
|
background-color: #555; /* Add a dark-grey background on hover */
|
||||||
|
}
|
|
@ -1,66 +1,75 @@
|
||||||
<div *ngIf="series">
|
<div *ngIf="series">
|
||||||
|
|
||||||
|
<ngb-tabset>
|
||||||
|
|
||||||
|
<div *ngFor="let season of series.seasonRequests">
|
||||||
|
<ngb-tab [id]="season.seasonNumber" [title]="season.seasonNumber">
|
||||||
|
<ng-template ngbTabContent>
|
||||||
|
<h2>Season: {{season.seasonNumber}}</h2>
|
||||||
|
|
||||||
|
<table class="table table-striped table-hover table-responsive table-condensed">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<a>
|
||||||
|
#
|
||||||
|
</a>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<a>
|
||||||
|
Title
|
||||||
|
</a>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<a>
|
||||||
|
Air Date
|
||||||
|
</a>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<a>
|
||||||
|
Status
|
||||||
|
</a>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let ep of season.episodes">
|
||||||
|
<td>
|
||||||
|
{{ep.episodeNumber}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ep.title}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ep.airDate | date: 'dd/MM/yyyy' }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span *ngIf="ep.available" class="label label-success">Available</span>
|
||||||
|
<span *ngIf="ep.approved && !ep.available" class="label label-info">Processing Request</span>
|
||||||
|
<div *ngIf="ep.requested && !ep.available; then requested else notRequested"></div>
|
||||||
|
<template #requested>
|
||||||
|
<span *ngIf="!ep.available" class="label label-warning">Pending Approval</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #notRequested>
|
||||||
|
<span *ngIf="!ep.available" class="label label-danger">Not Yet Requested</span>
|
||||||
|
</template>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
|
||||||
|
<button (click)="addRequest(ep)" [disabled]="ep.available || ep.requested || ep.approved" class="btn btn-sm btn-primary-outline">Request</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</ng-template>
|
||||||
|
</ngb-tab>
|
||||||
|
</div>
|
||||||
|
</ngb-tabset>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div *ngFor="let season of series.seasonRequests;">
|
<button id="requestFloatingBtn" class="btn btn-sm btn-success" title="Go to top">Request</button>
|
||||||
<h2>Season: {{season.seasonNumber}}</h2>
|
|
||||||
|
|
||||||
<table class="table table-striped table-hover table-responsive table-condensed">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
<a>
|
|
||||||
#
|
|
||||||
</a>
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
<a>
|
|
||||||
Title
|
|
||||||
</a>
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
<a>
|
|
||||||
Air Date
|
|
||||||
</a>
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
<a>
|
|
||||||
Status
|
|
||||||
</a>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr *ngFor="let ep of season.episodes">
|
|
||||||
<td>
|
|
||||||
{{ep.episodeNumber}}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ep.title}}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ep.airDate | date: 'dd/MM/yyyy' }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span *ngIf="ep.available" class="label label-success">Available</span>
|
|
||||||
<span *ngIf="ep.approved && !ep.available" class="label label-info">Processing Request</span>
|
|
||||||
<div *ngIf="ep.requested && !ep.available; then requested else notRequested"></div>
|
|
||||||
<template #requested>
|
|
||||||
<span *ngIf="!ep.available" class="label label-warning">Pending Approval</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #notRequested>
|
|
||||||
<span *ngIf="!ep.available" class="label label-danger">Not Yet Requested</span>
|
|
||||||
</template>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
|
|
||||||
<button (click)="addRequest(ep)" [disabled]="ep.available || ep.requested || ep.approved" class="btn btn-sm btn-info-outline">Request</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,11 +10,13 @@ import { NotificationService } from '../services/notification.service';
|
||||||
|
|
||||||
import { ISearchTvResult } from '../interfaces/ISearchTvResult';
|
import { ISearchTvResult } from '../interfaces/ISearchTvResult';
|
||||||
import { IRequestEngineResult } from '../interfaces/IRequestEngineResult';
|
import { IRequestEngineResult } from '../interfaces/IRequestEngineResult';
|
||||||
import { IEpisodesRequested } from"../interfaces/IRequestModel";
|
import { IEpisodesRequested } from "../interfaces/IRequestModel";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ombi',
|
selector: 'ombi',
|
||||||
moduleId: module.id,
|
moduleId: module.id,
|
||||||
templateUrl: './seriesinformation.component.html'
|
templateUrl: './seriesinformation.component.html',
|
||||||
|
styleUrls: ['./seriesinformation.component.css']
|
||||||
})
|
})
|
||||||
export class SeriesInformationComponent implements OnInit, OnDestroy {
|
export class SeriesInformationComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ export class UserManagementComponent implements OnInit {
|
||||||
|
|
||||||
updateUser() {
|
updateUser() {
|
||||||
this.showEditDialog = false;
|
this.showEditDialog = false;
|
||||||
|
this.identityService.updateUser(this.selectedUser).subscribe(x => this.selectedUser = x);
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
|
@ -64,7 +65,8 @@ export class UserManagementComponent implements OnInit {
|
||||||
emailAddress: "",
|
emailAddress: "",
|
||||||
password: "",
|
password: "",
|
||||||
userType: 1,
|
userType: 1,
|
||||||
username: ""
|
username: "",
|
||||||
|
|
||||||
}
|
}
|
||||||
this.resetClaims();
|
this.resetClaims();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue