work around the user management

pull/1425/head
tidusjar 7 years ago
parent 5b49d03f85
commit 5ec9123851

1
.gitignore vendored

@ -237,3 +237,4 @@ _Pvt_Extensions
*.ncrunchproject
*.ncrunchsolution
*.txt

@ -11,5 +11,6 @@ namespace Ombi.Core.IdentityResolver
Task<UserDto> GetUser(string username);
Task<IEnumerable<UserDto>> GetUsers();
Task DeleteUser(UserDto user);
Task<UserDto> UpdateUser(UserDto userDto);
}
}

@ -84,6 +84,12 @@ namespace Ombi.Core.IdentityResolver
await UserRepository.DeleteUser(Mapper.Map<User>(user));
}
public async Task<UserDto> UpdateUser(UserDto userDto)
{
var user = Mapper.Map<User>(userDto);
return Mapper.Map<UserDto>(await UserRepository.UpdateUser(user));
}
private UserHash HashPassword(string password)
{
// generate a 128-bit salt using a secure PRNG

@ -7,9 +7,15 @@ namespace Ombi.Core.Models.UI
public string Id { get; set; }
public string Username { get; set; }
public string Alias { get; set; }
public List<string> Claims { get; set; }
public List<ClaimCheckboxes> Claims { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
public UserType UserType { get; set; }
}
public class ClaimCheckboxes
{
public string Value { get; set; }
public bool Enabled { get; set; }
}
}

@ -1,5 +1,7 @@
using AutoMapper;
using System;
using System.Security.Claims;
using Ombi.Core.Models.UI;
namespace Ombi.Mapping
{
@ -23,4 +25,17 @@ namespace Ombi.Mapping
return default(DateTime);
}
}
public class ClaimsConverter : ITypeConverter<Claim, ClaimCheckboxes>
{
public ClaimCheckboxes Convert(Claim source, ClaimCheckboxes destination, ResolutionContext context)
{
return new ClaimCheckboxes
{
Enabled = true,
Value = source.Value
};
}
}
}

@ -16,11 +16,13 @@ namespace Ombi.Mapping.Profiles
CreateMap<User, UserDto>().ReverseMap();
CreateMap<UserDto, UserViewModel>()
.ForMember(dest => dest.Claims, opts => opts.MapFrom(src => src.Claims.Select(x => x.Value).ToList())); // Map the claims to a List<string>
CreateMap<Claim, ClaimCheckboxes>().ConvertUsing<ClaimsConverter>();
CreateMap<string, Claim>()
.ConstructUsing(str => new Claim(ClaimTypes.Role, str)); // This is used for the UserViewModel List<string> claims => UserDto List<claim>
CreateMap<UserDto, UserViewModel>().ForMember(x => x.Password, opt => opt.Ignore());
CreateMap<ClaimCheckboxes, Claim>()
.ConstructUsing(checkbox => checkbox.Enabled ? new Claim(ClaimTypes.Role, checkbox.Value) : null);
// This is used for the UserViewModel List<string> claims => UserDto List<claim>
CreateMap<UserViewModel, UserDto>();
CreateMap<string, DateTime>().ConvertUsing<StringToDateTimeConverter>();

@ -10,5 +10,6 @@ namespace Ombi.Store.Repository
Task<User> GetUser(string username);
Task<IEnumerable<User>> GetUsers();
Task DeleteUser(User user);
Task<User> UpdateUser(User user);
}
}

@ -64,5 +64,12 @@ namespace Ombi.Store.Repository
Db.Users.Remove(user);
await Db.SaveChangesAsync();
}
public async Task<User> UpdateUser(User user)
{
Db.Users.Update(user);
await Db.SaveChangesAsync();
return user;
}
}
}

@ -60,7 +60,7 @@ namespace Ombi.Auth
private async Task GenerateToken(HttpContext context)
{
var request = context.Request;
UserAuthModel userInfo; // TODO use a stong type
UserAuthModel userInfo;
using (var bodyReader = new StreamReader(request.Body))
{

@ -1,5 +1,7 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security.Claims;
using System.Threading.Tasks;
using AutoMapper;
@ -38,6 +40,7 @@ namespace Ombi.Controllers
/// This should never be called after this.
/// The reason why we return false if users exists is that this method doesn't have any
/// authorization and could be called from anywhere.
/// <remarks>We have [AllowAnonymous] since when going through the wizard we do not have a JWT Token yet</remarks>
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
@ -66,15 +69,46 @@ namespace Ombi.Controllers
[HttpGet("Users")]
public async Task<IEnumerable<UserViewModel>> GetAllUsers()
{
return Mapper.Map<IEnumerable<UserViewModel>>(await IdentityManager.GetUsers());
var type = typeof(OmbiClaims);
FieldInfo[] fieldInfos = type.GetFields(BindingFlags.Public |
BindingFlags.Static | BindingFlags.FlattenHierarchy);
var fields = fieldInfos.Where(fi => fi.IsLiteral && !fi.IsInitOnly).ToList();
var allClaims = fields.Select(x => x.Name).ToList();
var users = Mapper.Map<IEnumerable<UserViewModel>>(await IdentityManager.GetUsers()).ToList();
foreach (var user in users)
{
var userClaims = user.Claims.Select(x => x.Value);
var left = allClaims.Except(userClaims);
foreach (var c in left)
{
user.Claims.Add(new ClaimCheckboxes
{
Enabled = false,
Value = c
});
}
}
return users;
}
[HttpPost]
public async Task<UserViewModel> CreateUser([FromBody] UserViewModel user)
{
user.Id = null;
var userResult = await IdentityManager.CreateUser(Mapper.Map<UserDto>(user));
return Mapper.Map<UserViewModel>(userResult);
}
[HttpPut]
public async Task<UserViewModel> UpdateUser([FromBody] UserViewModel user)
{
var userResult = await IdentityManager.UpdateUser(Mapper.Map<UserDto>(user));
return Mapper.Map<UserViewModel>(userResult);
}
[HttpDelete]
public async Task<StatusCodeResult> DeleteUser([FromBody] UserViewModel user)
@ -82,6 +116,19 @@ namespace Ombi.Controllers
await IdentityManager.DeleteUser(Mapper.Map<UserDto>(user));
return Ok();
}
[HttpGet("claims")]
public IEnumerable<ClaimCheckboxes> GetAllClaims()
{
var type = typeof(OmbiClaims);
FieldInfo[] fieldInfos = type.GetFields(BindingFlags.Public |
BindingFlags.Static | BindingFlags.FlattenHierarchy);
var fields = fieldInfos.Where(fi => fi.IsLiteral && !fi.IsInitOnly).ToList();
var allClaims = fields.Select(x => x.Name).ToList();
return allClaims.Select(x => new ClaimCheckboxes() {Value = x});
}
}
}

@ -20,6 +20,7 @@ namespace Ombi
.UseStartup<Startup>()
.Build();
host.Run();
}
}

@ -719,4 +719,5 @@ body {
.ui-growl-item{
margin-top:35px $i;
}
}

@ -22,7 +22,10 @@
<li [routerLinkActive]="['active']"><a [routerLink]="['/search']"><i class="fa fa-search"></i> Search</a></li>
</ul>
<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 *ngIf="isAdmin || isPowerUser" class="nav navbar-nav">
<li [routerLinkActive]="['active']"><a [routerLink]="['/usermanagement']"><i class="fa fa-user"></i> User Management</a></li>
</ul>

@ -36,7 +36,7 @@ import { StatusService } from './services/status.service';
import { SettingsModule } from './settings/settings.module';
import { WizardModule } from './wizard/wizard.module';
import { ButtonModule } from 'primeng/primeng';
import { ButtonModule, DialogModule } from 'primeng/primeng';
import { GrowlModule } from 'primeng/components/growl/growl';
import { DataTableModule, SharedModule } from 'primeng/primeng';
@ -64,7 +64,8 @@ const routes: Routes = [
SharedModule,
InfiniteScrollModule,
AuthModule,
WizardModule
WizardModule,
DialogModule
],
declarations: [
AppComponent,

@ -6,7 +6,7 @@ enum envs {
live = 2
}
var envVar = "#{Environment}";
var envVar = '0';
var env = envs.local;
if (envs[envVar]) {
env = envs[envVar];

@ -2,7 +2,7 @@
id: string,
username: string,
alias: string,
claims: string[],
claims: ICheckbox[],
emailAddress: string,
password: string,
userType : UserType,
@ -14,3 +14,9 @@ export enum UserType {
PlexUser = 2,
EmbyUser = 3
}
export interface ICheckbox {
value: string,
enabled:boolean,
}

@ -4,7 +4,7 @@ import { Http } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import { ServiceAuthHelpers } from './service.helpers';
import { IUser } from '../interfaces/IUser';
import { IUser, ICheckbox } from '../interfaces/IUser';
@Injectable()
@ -24,6 +24,18 @@ export class IdentityService extends ServiceAuthHelpers {
return this.http.get(`${this.url}/Users`).map(this.extractData);
}
getAllAvailableClaims(): Observable<ICheckbox[]> {
return this.http.get(`${this.url}/Claims`).map(this.extractData);
}
createUser(user: IUser): Observable<IUser> {
return this.http.post(this.url, JSON.stringify(user), { headers: this.headers }).map(this.extractData);
}
updateUser(user: IUser): Observable<IUser> {
return this.http.put(this.url, JSON.stringify(user), { headers: this.headers }).map(this.extractData);
}
hasRole(role: string): boolean {
var roles = localStorage.getItem("roles") as string[];
if (roles) {

@ -15,60 +15,170 @@
</div>
</div>
<button type="button" class="btn btn-success-outline" (click)="showCreateDialogue=true">Add User</button>
<!-- Table -->
<table class="table table-striped table-hover table-responsive table-condensed">
<thead>
<tr>
<th>
<a (click)="changeSort('username')">
Username
<!--<span ng-show="sortType == 'username' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'username' && sortReverse" class="fa fa-caret-up"></span>-->
</a>
</th>
<th>
<a>
Alias
</a>
</th>
<th>
<a>
Email
</a>
</th>
<th>
Roles
</th>
<th>
<a>
User Type
</a>
</th>
</tr>
<tr>
<th>
<a>
Username
<!--<span ng-show="sortType == 'username' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'username' && sortReverse" class="fa fa-caret-up"></span>-->
</a>
</th>
<th>
<a>
Alias
</a>
</th>
<th>
<a>
Email
</a>
</th>
<th>
Roles
</th>
<th>
<a>
User Type
</a>
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let u of users">
<td>
{{u.username}}
</td>
<td>
{{u.alias}}
</td>
<td>
{{u.emailAddress}}
</td>
<td>
<span *ngFor="let claim of u.claims">{{claim}}</span>
</td>
<td ng-hide="hideColumns">
<span ng-if="u.userType === 1">Local User</span>
<span ng-if="u.userType === 2">Plex User</span>
<span ng-if="u.userType === 3">Emby User</span>
</td>
<td>
<a (click)="edit(u)" class="btn btn-sm btn-info-outline">Details/Edit</a>
</td>
</tr>
<tr *ngFor="let u of users">
<td>
{{u.username}}
</td>
<td>
{{u.alias}}
</td>
<td>
{{u.emailAddress}}
</td>
<td>
<div *ngFor="let claim of u.claims"><span *ngIf="claim.enabled">{{claim.value}}</span></div>
</td>
<td ng-hide="hideColumns">
<span *ngIf="u.userType === 1">Local User</span>
<span *ngIf="u.userType === 2">Plex User</span>
<span *ngIf="u.userType === 3">Emby User</span>
</td>
<td>
<a (click)="edit(u)" class="btn btn-sm btn-info-outline">Details/Edit</a>
</td>
</tr>
</tbody>
</table>
</table>
<div class="modal fade in" *ngIf="showEditDialog" style="display: block;">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" (click)="showEditDialog=false">&times;</button>
<h4 class="modal-title">Editing User {{selectedUser?.username}}</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label for="username" class="control-label">Username</label>
<div>
<input type="text" [(ngModel)]="selectedUser.username" [readonly]="true" class="form-control form-control-custom " id="username" name="username" value="{{selectedUser?.username}}">
</div>
</div>
<div class="form-group">
<label for="alias" class="control-label">Alias</label>
<div>
<input type="text" [(ngModel)]="selectedUser.alias" class="form-control form-control-custom " id="alias" name="alias" value="{{selectedUser?.alias}}">
</div>
</div>
<div class="form-group">
<label for="alias" class="control-label">Email Address</label>
<div>
<input type="text" [(ngModel)]="selectedUser.emailAddress" class="form-control form-control-custom " id="emailAddress" name="emailAddress" value="{{selectedUser?.emailAddress}}">
</div>
</div>
<div *ngFor="let c of selectedUser.claims">
<div class="form-group">
<div class="checkbox">
<label for="claim{{c.value}}">{{c.value}}</label>
<input type="checkbox" [(ngModel)]="c.enabled" [value]="c.value" [checked]="c.value" id="claim{{c.value}}" name="claim{{c.value}}" ng-checked="c.enabled">
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger-outline" (click)="showEditDialog=false">Close</button>
<button type="button" class="btn btn-primary-outline" (click)="updateUser()">Save changes</button>
</div>
</div>
</div>
</div>
<div id="lightbox">
<div class="modal fade in " *ngIf="showCreateDialogue" style="display: block;">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" (click)="showCreateDialogue=false">&times;</button>
<h4 class="modal-title">Create User</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label for="username" class="control-label">Username</label>
<div>
<input type="text" [(ngModel)]="createdUser.username" class="form-control form-control-custom " id="username" name="username" value="{{createdUser?.username}}">
</div>
</div>
<div class="form-group">
<label for="alias" class="control-label">Alias</label>
<div>
<input type="text" [(ngModel)]="createdUser.alias" class="form-control form-control-custom " id="alias" name="alias" value="{{createdUser?.alias}}">
</div>
</div>
<div class="form-group">
<label for="emailAddress" class="control-label">Email Address</label>
<div>
<input type="text" [(ngModel)]="createdUser.emailAddress" class="form-control form-control-custom " id="emailAddress" name="emailAddress" value="{{createdUser?.emailAddress}}">
</div>
</div>
<div class="form-group">
<label for="password" class="control-label">Password</label>
<div>
<input type="password" [(ngModel)]="createdUser.password" class="form-control form-control-custom " id="password" name="password" value="{{createdUser?.password}}">
</div>
</div>
<div *ngFor="let c of availableClaims">
<div class="form-group">
<div class="checkbox">
<label for="create{{c.value}}">{{c.value}}</label>
<input type="checkbox" [(ngModel)]="c.enabled" [value]="c.value" [checked]="c.value" id="create{{c.value}}" name="create{{c.value}}" ng-checked="c.enabled">
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger-outline" (click)="showCreateDialogue=false">Close</button>
<button type="button" class="btn btn-primary-outline" (click)="create()">Add User</button>
</div>
</div>
</div>
</div>
</div>

@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { IUser } from '../interfaces/IUser';
import { IUser, ICheckbox } from '../interfaces/IUser';
import { IdentityService } from '../services/identity.service';
@Component({
@ -16,17 +16,57 @@ export class UserManagementComponent implements OnInit {
this.identityService.getUsers().subscribe(x => {
this.users = x;
});
this.identityService.getAllAvailableClaims().subscribe(x => this.availableClaims = x);
this.resetCreatedUser();
}
users: IUser[];
selectedUser: IUser;
createdUser: IUser;
availableClaims : ICheckbox[];
showEditDialog = false;
showCreateDialogue = false;
edit(user: IUser) {
this.selectedUser = user;
this.showEditDialog = true;
}
updateUser() {
this.showEditDialog = false;
}
changeSort(username: string) {
//??????
create() {
this.createdUser.claims = this.availableClaims;
this.identityService.createUser(this.createdUser).subscribe(x => {
this.users.push(x); // Add the new user
this.showCreateDialogue = false;
this.resetCreatedUser();
});
}
private resetClaims() {
//this.availableClaims.forEach(x => {
// x.enabled = false;
//});
}
private resetCreatedUser() {
this.createdUser = {
id: "-1",
alias: "",
claims: [],
emailAddress: "",
password: "",
userType: 1,
username: ""
}
this.resetClaims();
}
//private removeRequestFromUi(key : IRequestModel) {

Loading…
Cancel
Save