mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-05-24 00:52:23 -04:00
Remove No Authentication mode from Kavita (#1006)
* Moved the Server Settings out into a button on nav header * Refactored Mange Users page to the new design (skeleton). Implemented skeleton code for Invite User. * Hashed out more of the code, but need to move all the email code to a Kavita controlled API server due to password credentials. * Cleaned up some warnings * When no user exists for an api key in Plugin controller, throw 401. * Hooked in the ability to check if the Kavita instance can be accessed externally so we can determine if the user can invite or not. * Hooked up some logic if the user's server isn't accessible, then default to old flow * Basic flow is working for confirm email. Needs validation, error handling, etc. * Refactored Password validation to account service * Cleaned up the code in confirm-email to work much better. * Refactored the login page to have a container functionality, so we can reuse the styles on multiple pages (registration pages). Hooked up the code for confirm email. * Messy code, but making progress. Refactored Register to be used only for first time user registration. Added a new register component to handle first time flow only. * Invite works much better, still needs a bit of work for non-accessible server setup. Started work on underlying manage users page to meet new design. * Changed (you) to a star to indicate who you're logged in as. * Inviting a user is now working and tested fully. * Removed the register member component as we now have invite and confirm components. * Editing a user is now working. Username change and Role/Library access from within one screen. Email changing is on hold. * Cleaned up code for edit user and disabled email field for now. * Cleaned up the code to indicate changing a user's email is not possible. * Implemented a migration for existing accounts so they can validate their emails and still login. * Change url for email server * Implemented the ability to resend an email confirmation code (or regenerate for non accessible servers). Fixed an overflow on the confirm dialog. * Removed all code around disabling authentication. Users that were already disabled can look up their password on the wiki.
This commit is contained in:
parent
1a72c53711
commit
c8de3fb097
@ -191,14 +191,6 @@ namespace API.Controllers
|
||||
"You are missing an email on your account. Please wait while we migrate your account.");
|
||||
}
|
||||
|
||||
var isAdmin = await _unitOfWork.UserRepository.IsUserAdminAsync(user);
|
||||
var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
|
||||
if (!settings.EnableAuthentication && !isAdmin)
|
||||
{
|
||||
_logger.LogDebug("User {UserName} is logging in with authentication disabled", loginDto.Username);
|
||||
loginDto.Password = AccountService.DefaultPassword;
|
||||
}
|
||||
|
||||
var result = await _signInManager
|
||||
.CheckPasswordSignInAsync(user, loginDto.Password, false);
|
||||
|
||||
|
@ -23,17 +23,15 @@ namespace API.Controllers
|
||||
private readonly ILogger<SettingsController> _logger;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly ITaskScheduler _taskScheduler;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly IDirectoryService _directoryService;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public SettingsController(ILogger<SettingsController> logger, IUnitOfWork unitOfWork, ITaskScheduler taskScheduler,
|
||||
IAccountService accountService, IDirectoryService directoryService, IMapper mapper)
|
||||
IDirectoryService directoryService, IMapper mapper)
|
||||
{
|
||||
_logger = logger;
|
||||
_unitOfWork = unitOfWork;
|
||||
_taskScheduler = taskScheduler;
|
||||
_accountService = accountService;
|
||||
_directoryService = directoryService;
|
||||
_mapper = mapper;
|
||||
}
|
||||
@ -84,7 +82,6 @@ namespace API.Controllers
|
||||
|
||||
// We do not allow CacheDirectory changes, so we will ignore.
|
||||
var currentSettings = await _unitOfWork.SettingsRepository.GetSettingsAsync();
|
||||
var updateAuthentication = false;
|
||||
var updateBookmarks = false;
|
||||
var originalBookmarkDirectory = _directoryService.BookmarkDirectory;
|
||||
|
||||
@ -163,13 +160,6 @@ namespace API.Controllers
|
||||
|
||||
}
|
||||
|
||||
if (setting.Key == ServerSettingKey.EnableAuthentication && updateSettingsDto.EnableAuthentication + string.Empty != setting.Value)
|
||||
{
|
||||
setting.Value = updateSettingsDto.EnableAuthentication + string.Empty;
|
||||
_unitOfWork.SettingsRepository.Update(setting);
|
||||
updateAuthentication = true;
|
||||
}
|
||||
|
||||
if (setting.Key == ServerSettingKey.AllowStatCollection && updateSettingsDto.AllowStatCollection + string.Empty != setting.Value)
|
||||
{
|
||||
setting.Value = updateSettingsDto.AllowStatCollection + string.Empty;
|
||||
@ -191,21 +181,6 @@ namespace API.Controllers
|
||||
{
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
if (updateAuthentication)
|
||||
{
|
||||
var users = await _unitOfWork.UserRepository.GetNonAdminUsersAsync();
|
||||
foreach (var user in users)
|
||||
{
|
||||
var errors = await _accountService.ChangeUserPassword(user, AccountService.DefaultPassword);
|
||||
if (!errors.Any()) continue;
|
||||
|
||||
await _unitOfWork.RollbackAsync();
|
||||
return BadRequest(errors);
|
||||
}
|
||||
|
||||
_logger.LogInformation("Server authentication changed. Updated all non-admins to default password");
|
||||
}
|
||||
|
||||
if (updateBookmarks)
|
||||
{
|
||||
_directoryService.ExistOrCreate(bookmarkDirectory);
|
||||
@ -253,12 +228,5 @@ namespace API.Controllers
|
||||
var settingsDto = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
|
||||
return Ok(settingsDto.EnableOpds);
|
||||
}
|
||||
|
||||
[HttpGet("authentication-enabled")]
|
||||
public async Task<ActionResult<bool>> GetAuthenticationEnabled()
|
||||
{
|
||||
var settingsDto = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
|
||||
return Ok(settingsDto.EnableAuthentication);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,21 +47,6 @@ namespace API.Controllers
|
||||
}
|
||||
|
||||
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpGet("names")]
|
||||
public async Task<ActionResult<IEnumerable<MemberDto>>> GetUserNames()
|
||||
{
|
||||
// This is only for disabled auth flow - being removed
|
||||
var setting = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
|
||||
if (setting.EnableAuthentication)
|
||||
{
|
||||
return Unauthorized("This API cannot be used given your server's configuration");
|
||||
}
|
||||
var members = await _unitOfWork.UserRepository.GetEmailConfirmedMemberDtosAsync();
|
||||
return Ok(members.Select(m => m.Username));
|
||||
}
|
||||
|
||||
[HttpGet("has-reading-progress")]
|
||||
public async Task<ActionResult<bool>> HasReadingProgress(int libraryId)
|
||||
{
|
||||
@ -104,6 +89,5 @@ namespace API.Controllers
|
||||
|
||||
return BadRequest("There was an issue saving preferences.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -23,11 +23,6 @@ namespace API.DTOs.Settings
|
||||
/// Enables OPDS connections to be made to the server.
|
||||
/// </summary>
|
||||
public bool EnableOpds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables Authentication on the server. Defaults to true.
|
||||
/// </summary>
|
||||
public bool EnableAuthentication { get; set; }
|
||||
/// <summary>
|
||||
/// Base Url for the kavita. Requires restart to take effect.
|
||||
/// </summary>
|
||||
|
@ -47,6 +47,7 @@ namespace API.Entities.Enums
|
||||
/// <summary>
|
||||
/// Is Authentication needed for non-admin accounts
|
||||
/// </summary>
|
||||
/// <remarks>Deprecated. This is no longer used v0.5.1+. Assume Authentication is always in effect</remarks>
|
||||
[Description("EnableAuthentication")]
|
||||
EnableAuthentication = 8,
|
||||
/// <summary>
|
||||
|
@ -36,9 +36,6 @@ namespace API.Helpers.Converters
|
||||
case ServerSettingKey.EnableOpds:
|
||||
destination.EnableOpds = bool.Parse(row.Value);
|
||||
break;
|
||||
case ServerSettingKey.EnableAuthentication:
|
||||
destination.EnableAuthentication = bool.Parse(row.Value);
|
||||
break;
|
||||
case ServerSettingKey.BaseUrl:
|
||||
destination.BaseUrl = row.Value;
|
||||
break;
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
@ -6,7 +7,7 @@ using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities.Enums;
|
||||
using API.Comparators;
|
||||
using API.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
@ -681,7 +682,7 @@ namespace API.Services
|
||||
FileSystem.Path.Join(directoryName, "test.txt"),
|
||||
string.Empty);
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
ClearAndDeleteDirectory(directoryName);
|
||||
return false;
|
||||
|
@ -6,7 +6,6 @@ export interface ServerSettings {
|
||||
port: number;
|
||||
allowStatCollection: boolean;
|
||||
enableOpds: boolean;
|
||||
enableAuthentication: boolean;
|
||||
baseUrl: string;
|
||||
bookmarksDirectory: string;
|
||||
}
|
||||
|
@ -65,15 +65,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="authentication" aria-describedby="authentication-info">Authentication</label>
|
||||
<p class="accent" id="authentication-info">By disabling authentication, all non-admin users will be able to login by just their username. No password will be required to authenticate.</p>
|
||||
<div class="form-check">
|
||||
<input id="authentication" type="checkbox" aria-label="User Authentication" class="form-check-input" formControlName="enableAuthentication">
|
||||
<label for="authentication" class="form-check-label">Enable Authentication</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4>Reoccuring Tasks</h4>
|
||||
<div class="form-group">
|
||||
<label for="settings-tasks-scan">Library Scan</label> <i class="fa fa-info-circle" placement="right" [ngbTooltip]="taskScanTooltip" role="button" tabindex="0"></i>
|
||||
|
@ -40,7 +40,6 @@ export class ManageSettingsComponent implements OnInit {
|
||||
this.settingsForm.addControl('loggingLevel', new FormControl(this.serverSettings.loggingLevel, [Validators.required]));
|
||||
this.settingsForm.addControl('allowStatCollection', new FormControl(this.serverSettings.allowStatCollection, [Validators.required]));
|
||||
this.settingsForm.addControl('enableOpds', new FormControl(this.serverSettings.enableOpds, [Validators.required]));
|
||||
this.settingsForm.addControl('enableAuthentication', new FormControl(this.serverSettings.enableAuthentication, [Validators.required]));
|
||||
this.settingsForm.addControl('baseUrl', new FormControl(this.serverSettings.baseUrl, [Validators.required]));
|
||||
});
|
||||
}
|
||||
@ -54,29 +53,16 @@ export class ManageSettingsComponent implements OnInit {
|
||||
this.settingsForm.get('loggingLevel')?.setValue(this.serverSettings.loggingLevel);
|
||||
this.settingsForm.get('allowStatCollection')?.setValue(this.serverSettings.allowStatCollection);
|
||||
this.settingsForm.get('enableOpds')?.setValue(this.serverSettings.enableOpds);
|
||||
this.settingsForm.get('enableAuthentication')?.setValue(this.serverSettings.enableAuthentication);
|
||||
this.settingsForm.get('baseUrl')?.setValue(this.serverSettings.baseUrl);
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
const modelSettings = this.settingsForm.value;
|
||||
|
||||
if (this.settingsForm.get('enableAuthentication')?.dirty && this.settingsForm.get('enableAuthentication')?.value === false) {
|
||||
if (!await this.confirmService.confirm('Disabling Authentication opens your server up to unauthorized access and possible hacking. Are you sure you want to continue with this?')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const informUserAfterAuthenticationEnabled = this.settingsForm.get('enableAuthentication')?.dirty && this.settingsForm.get('enableAuthentication')?.value && !this.serverSettings.enableAuthentication;
|
||||
|
||||
this.settingsService.updateServerSettings(modelSettings).pipe(take(1)).subscribe(async (settings: ServerSettings) => {
|
||||
this.serverSettings = settings;
|
||||
this.resetForm();
|
||||
this.toastr.success('Server settings updated');
|
||||
|
||||
if (informUserAfterAuthenticationEnabled) {
|
||||
await this.confirmService.alert('You have just re-enabled authentication. All non-admin users have been re-assigned a password of "[k.2@RZ!mxCQkJzE". This is a publicly known password. Please change their users passwords or request them to.');
|
||||
}
|
||||
}, (err: any) => {
|
||||
console.error('error: ', err);
|
||||
});
|
||||
|
@ -40,10 +40,4 @@ export class SettingsService {
|
||||
getOpdsEnabled() {
|
||||
return this.http.get<boolean>(this.baseUrl + 'settings/opds-enabled', {responseType: 'text' as 'json'});
|
||||
}
|
||||
|
||||
getAuthenticationEnabled() {
|
||||
return this.http.get<string>(this.baseUrl + 'settings/authentication-enabled', {responseType: 'text' as 'json'}).pipe(map((res: string) => {
|
||||
return res === 'true';
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import { PersonRolePipe } from './person-role.pipe';
|
||||
import { SeriesMetadataDetailComponent } from './series-metadata-detail/series-metadata-detail.component';
|
||||
import { AllSeriesComponent } from './all-series/all-series.component';
|
||||
import { PublicationStatusPipe } from './publication-status.pipe';
|
||||
import { RegistrationModule } from './registration/registration.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
@ -80,6 +81,7 @@ import { PublicationStatusPipe } from './publication-status.pipe';
|
||||
CardsModule,
|
||||
CollectionsModule,
|
||||
ReadingListModule,
|
||||
RegistrationModule,
|
||||
|
||||
ToastrModule.forRoot({
|
||||
positionClass: 'toast-bottom-right',
|
||||
|
@ -24,6 +24,9 @@ import { ConfirmMigrationEmailComponent } from './confirm-migration-email/confir
|
||||
RegistrationRoutingModule,
|
||||
NgbTooltipModule,
|
||||
ReactiveFormsModule
|
||||
],
|
||||
exports: [
|
||||
SplashContainerComponent
|
||||
]
|
||||
})
|
||||
export class RegistrationModule { }
|
||||
|
@ -1,38 +1,24 @@
|
||||
<div class="mx-auto login">
|
||||
|
||||
<ng-container *ngIf="isLoaded">
|
||||
<form [formGroup]="loginForm" (ngSubmit)="login()" novalidate class="needs-validation" *ngIf="!firstTimeFlow">
|
||||
<div class="row row-cols-4 row-cols-md-4 row-cols-sm-2 row-cols-xs-2">
|
||||
<ng-container *ngFor="let member of memberNames">
|
||||
<div class="col align-self-center card p-3 m-3" style="max-width: 12rem;">
|
||||
<span tabindex="0" (click)="select(member)" a11y-click="13,32">
|
||||
<div class="logo-container">
|
||||
<h3 class="card-title text-center">{{member | sentenceCase}}</h3>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
<div class="card-text" #collapse="ngbCollapse" [(ngbCollapse)]="isCollapsed[member]" (keyup.enter)="$event.stopPropagation()">
|
||||
<div class="form-group" [ngStyle]="authDisabled ? {display: 'none'} : {}">
|
||||
<label for="username--{{member}}">Username</label>
|
||||
<input class="form-control" formControlName="username" id="username--{{member}}" type="text" [readonly]="authDisabled">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password--{{member}}">Password</label>
|
||||
<input class="form-control" formControlName="password" id="password--{{member}}" type="password" autofocus>
|
||||
<div *ngIf="authDisabled" class="invalid-feedback">
|
||||
Authentication is disabled. Only type password if this is an admin account.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="float-right">
|
||||
<button class="btn btn-primary alt" type="submit--{{member}}">Login</button>
|
||||
</div>
|
||||
<app-splash-container>
|
||||
<ng-container title><h2>Login</h2></ng-container>
|
||||
<ng-container body>
|
||||
<ng-container *ngIf="isLoaded">
|
||||
<form [formGroup]="loginForm" (ngSubmit)="login()" novalidate class="needs-validation" *ngIf="!firstTimeFlow">
|
||||
<div class="card-text">
|
||||
<div class="form-group" style="width:100%">
|
||||
<label for="username">Username</label>
|
||||
<input class="form-control" formControlName="username" id="username" type="text">
|
||||
</div>
|
||||
|
||||
<div class="form-group" style="width:100%">
|
||||
<label for="password">Password</label>
|
||||
<input class="form-control" formControlName="password" id="password" type="password" autofocus>
|
||||
</div>
|
||||
|
||||
<div class="float-right">
|
||||
<button class="btn btn-secondary alt" type="submit">Login</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
</app-splash-container>
|
@ -1,84 +1,89 @@
|
||||
@use "../../theme/colors";
|
||||
// @use "../../theme/colors";
|
||||
|
||||
.login {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: -61px; // To offset the navbar
|
||||
height: calc(100vh);
|
||||
min-height: 289px;
|
||||
position: relative;
|
||||
width: 100vw;
|
||||
max-width: 100vw;
|
||||
// .login {
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// justify-content: center;
|
||||
// margin-top: -61px; // To offset the navbar
|
||||
// height: calc(100vh);
|
||||
// min-height: 289px;
|
||||
// position: relative;
|
||||
// width: 100vw;
|
||||
// max-width: 100vw;
|
||||
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
background-image: url('../../assets/images/login-bg.jpg');
|
||||
background-size: cover;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
opacity: 0.1;
|
||||
width: 100%;
|
||||
}
|
||||
// &::before {
|
||||
// content: "";
|
||||
// background-image: url('../../assets/images/login-bg.jpg');
|
||||
// background-size: cover;
|
||||
// position: absolute;
|
||||
// top: 0;
|
||||
// right: 0;
|
||||
// bottom: 0;
|
||||
// opacity: 0.1;
|
||||
// width: 100%;
|
||||
// }
|
||||
|
||||
.logo-container {
|
||||
.logo {
|
||||
display:inline-block;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
// .logo-container {
|
||||
// .logo {
|
||||
// display:inline-block;
|
||||
// height: 50px;
|
||||
// }
|
||||
// }
|
||||
|
||||
.row {
|
||||
margin-top: 10vh;
|
||||
}
|
||||
// .row {
|
||||
// margin-top: 10vh;
|
||||
// }
|
||||
|
||||
.card {
|
||||
background-color: colors.$primary-color;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
min-width: 300px;
|
||||
// .card {
|
||||
// background-color: colors.$primary-color;
|
||||
// color: #fff;
|
||||
// cursor: pointer;
|
||||
// min-width: 300px;
|
||||
|
||||
&:focus {
|
||||
border: 2px solid white;
|
||||
// &:focus {
|
||||
// border: 2px solid white;
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
|
||||
.card-title {
|
||||
font-family: 'Spartan', sans-serif;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 280px;
|
||||
}
|
||||
// .card-title {
|
||||
// font-family: 'Spartan', sans-serif;
|
||||
// font-weight: bold;
|
||||
// display: inline-block;
|
||||
// vertical-align: middle;
|
||||
// width: 280px;
|
||||
// }
|
||||
|
||||
.card-text {
|
||||
font-family: "EBGaramond", "Helvetica Neue", sans-serif;
|
||||
}
|
||||
// .card-text {
|
||||
// font-family: "EBGaramond", "Helvetica Neue", sans-serif;
|
||||
// }
|
||||
|
||||
.alt {
|
||||
background-color: #424c72;
|
||||
border-color: #444f75;
|
||||
}
|
||||
// .alt {
|
||||
// background-color: #424c72;
|
||||
// border-color: #444f75;
|
||||
// }
|
||||
|
||||
.alt:hover {
|
||||
background-color: #3b4466;
|
||||
}
|
||||
// .alt:hover {
|
||||
// background-color: #3b4466;
|
||||
// }
|
||||
|
||||
.alt:focus {
|
||||
background-color: #343c59;
|
||||
box-shadow: 0 0 0 0.2rem rgb(68 79 117 / 50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
// .alt:focus {
|
||||
// background-color: #343c59;
|
||||
// box-shadow: 0 0 0 0.2rem rgb(68 79 117 / 50%);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
.invalid-feedback {
|
||||
display: inline-block;
|
||||
color: #343c59;
|
||||
}
|
||||
// .invalid-feedback {
|
||||
// display: inline-block;
|
||||
// color: #343c59;
|
||||
// }
|
||||
|
||||
// input {
|
||||
// background-color: #fff !important;
|
||||
// color: black;
|
||||
// }
|
||||
|
||||
input {
|
||||
background-color: #fff !important;
|
||||
|
@ -3,7 +3,7 @@ import { FormGroup, FormControl, Validators } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { first, take } from 'rxjs/operators';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { SettingsService } from '../admin/settings.service';
|
||||
import { AddEmailToAccountMigrationModalComponent } from '../registration/add-email-to-account-migration-modal/add-email-to-account-migration-modal.component';
|
||||
import { User } from '../_models/user';
|
||||
@ -11,6 +11,7 @@ import { AccountService } from '../_services/account.service';
|
||||
import { MemberService } from '../_services/member.service';
|
||||
import { NavService } from '../_services/nav.service';
|
||||
|
||||
// TODO: Move this into registration module
|
||||
@Component({
|
||||
selector: 'app-user-login',
|
||||
templateUrl: './user-login.component.html',
|
||||
@ -21,12 +22,11 @@ export class UserLoginComponent implements OnInit {
|
||||
model: any = {username: '', password: ''};
|
||||
loginForm: FormGroup = new FormGroup({
|
||||
username: new FormControl('', [Validators.required]),
|
||||
password: new FormControl('', [Validators.required])
|
||||
password: new FormControl('', [Validators.required])
|
||||
});
|
||||
|
||||
memberNames: Array<string> = [];
|
||||
isCollapsed: {[key: string]: boolean} = {};
|
||||
authDisabled: boolean = false;
|
||||
/**
|
||||
* If there are no admins on the server, this will enable the registration to kick in.
|
||||
*/
|
||||
@ -36,7 +36,7 @@ export class UserLoginComponent implements OnInit {
|
||||
*/
|
||||
isLoaded: boolean = false;
|
||||
|
||||
constructor(private accountService: AccountService, private router: Router, private memberService: MemberService,
|
||||
constructor(private accountService: AccountService, private router: Router, private memberService: MemberService,
|
||||
private toastr: ToastrService, private navService: NavService, private settingsService: SettingsService, private modalService: NgbModal) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
@ -47,36 +47,17 @@ export class UserLoginComponent implements OnInit {
|
||||
}
|
||||
});
|
||||
|
||||
this.settingsService.getAuthenticationEnabled().pipe(take(1)).subscribe((enabled: boolean) => {
|
||||
// There is a bug where this is coming back as a string not a boolean.
|
||||
this.authDisabled = !enabled;
|
||||
if (this.authDisabled) {
|
||||
this.loginForm.get('password')?.setValidators([]);
|
||||
this.memberService.adminExists().pipe(take(1)).subscribe(adminExists => {
|
||||
this.firstTimeFlow = !adminExists;
|
||||
|
||||
// This API is only useable on disabled authentication
|
||||
this.memberService.getMemberNames().pipe(take(1)).subscribe(members => {
|
||||
this.memberNames = members;
|
||||
const isOnlyOne = this.memberNames.length === 1;
|
||||
this.memberNames.forEach(name => this.isCollapsed[name] = !isOnlyOne);
|
||||
this.firstTimeFlow = members.length === 0;
|
||||
this.isLoaded = true;
|
||||
});
|
||||
} else {
|
||||
this.memberService.adminExists().pipe(take(1)).subscribe(adminExists => {
|
||||
this.firstTimeFlow = !adminExists;
|
||||
|
||||
if (this.firstTimeFlow) {
|
||||
this.router.navigateByUrl('registration/register');
|
||||
return;
|
||||
}
|
||||
|
||||
this.setupAuthenticatedLoginFlow();
|
||||
this.isLoaded = true;
|
||||
});
|
||||
if (this.firstTimeFlow) {
|
||||
this.router.navigateByUrl('registration/register');
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this.setupAuthenticatedLoginFlow();
|
||||
this.isLoaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
setupAuthenticatedLoginFlow() {
|
||||
@ -92,11 +73,6 @@ export class UserLoginComponent implements OnInit {
|
||||
onAdminCreated(user: User | null) {
|
||||
if (user != null) {
|
||||
this.firstTimeFlow = false;
|
||||
if (this.authDisabled) {
|
||||
this.isCollapsed[user.username] = true;
|
||||
this.select(user.username);
|
||||
this.memberNames.push(user.username);
|
||||
}
|
||||
} else {
|
||||
this.toastr.error('There was an issue creating the new user. Please refresh and try again.');
|
||||
}
|
||||
@ -118,43 +94,14 @@ export class UserLoginComponent implements OnInit {
|
||||
}
|
||||
}, err => {
|
||||
if (err.error === 'You are missing an email on your account. Please wait while we migrate your account.') {
|
||||
// TODO: Implement this flow
|
||||
const modalRef = this.modalService.open(AddEmailToAccountMigrationModalComponent, { scrollable: true, size: 'md' });
|
||||
modalRef.componentInstance.username = this.model.username;
|
||||
modalRef.componentInstance.username = this.model.username;
|
||||
modalRef.closed.pipe(take(1)).subscribe(() => {
|
||||
|
||||
|
||||
});
|
||||
} else {
|
||||
this.toastr.error(err.error);
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Move this into account service so it always happens
|
||||
this.accountService.currentUser$
|
||||
.pipe(first(x => (x !== null && x !== undefined && typeof x !== 'undefined')))
|
||||
.subscribe(currentUser => {
|
||||
this.navService.setDarkMode(currentUser.preferences.siteDarkMode);
|
||||
});
|
||||
}
|
||||
|
||||
select(member: string) {
|
||||
// This is a special case
|
||||
if (member === ' Login ' && !this.authDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loginForm.get('username')?.setValue(member);
|
||||
|
||||
this.isCollapsed[member] = !this.isCollapsed[member];
|
||||
this.collapseAllButName(member);
|
||||
// ?! Scroll to the newly opened element?
|
||||
}
|
||||
|
||||
collapseAllButName(name: string) {
|
||||
Object.keys(this.isCollapsed).forEach(key => {
|
||||
if (key !== name) {
|
||||
this.isCollapsed[key] = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +194,7 @@
|
||||
<app-series-bookmarks></app-series-bookmarks>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="tab.fragment === 'password'">
|
||||
<ng-container *ngIf="isAuthenticationEnabled || isAdmin; else authDisabled">
|
||||
<ng-container *ngIf="isAdmin">
|
||||
<p>Change your Password</p>
|
||||
<div class="alert alert-danger" role="alert" *ngIf="resetPasswordErrors.length > 0">
|
||||
<div *ngFor="let error of resetPasswordErrors">{{error}}</div>
|
||||
@ -227,9 +227,6 @@
|
||||
</div>
|
||||
</form>
|
||||
</ng-container>
|
||||
<ng-template #authDisabled>
|
||||
<p class="text-warning">Authentication is disabled on this server. A password is not required to authenticate.</p>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="tab.fragment === 'clients'">
|
||||
<p>All 3rd Party clients will either use the API key or the Connection Url below. These are like passwords, keep it private.</p>
|
||||
|
@ -28,7 +28,6 @@ export class UserPreferencesComponent implements OnInit, OnDestroy {
|
||||
passwordChangeForm: FormGroup = new FormGroup({});
|
||||
user: User | undefined = undefined;
|
||||
isAdmin: boolean = false;
|
||||
isAuthenticationEnabled: boolean = true;
|
||||
|
||||
passwordsMatch = false;
|
||||
resetPasswordErrors: string[] = [];
|
||||
@ -78,9 +77,6 @@ export class UserPreferencesComponent implements OnInit, OnDestroy {
|
||||
this.settingsService.getOpdsEnabled().subscribe(res => {
|
||||
this.opdsEnabled = res;
|
||||
});
|
||||
this.settingsService.getAuthenticationEnabled().subscribe(res => {
|
||||
this.isAuthenticationEnabled = res;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
Loading…
x
Reference in New Issue
Block a user