mirror of
https://github.com/apache/archiva.git
synced 2025-02-07 18:49:48 +00:00
Adding user info and password change
This commit is contained in:
parent
cb0e4d19d7
commit
c7344aa1c4
@ -0,0 +1,28 @@
|
||||
package org.apache.archiva.rest.api.services.v2;/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* Service for configuration of redback and security related settings.
|
||||
*
|
||||
* @author Martin Stockhammer <martin_s@apache.org>
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface SecurityConfigurationService
|
||||
{
|
||||
}
|
@ -29,6 +29,7 @@ import {BrowseComponent} from "./modules/repo/browse/browse.component";
|
||||
import {UploadComponent} from "./modules/repo/upload/upload.component";
|
||||
import {RoutingGuardService as Guard} from "./services/routing-guard.service";
|
||||
import {SecurityConfigurationComponent} from "./modules/security/security-configuration/security-configuration.component";
|
||||
import {UserInfoComponent} from "@app/modules/shared/user-info/user-info.component";
|
||||
|
||||
/**
|
||||
* You can use Guard (RoutingGuardService) for permission checking. The service needs data with one parameter 'perm',
|
||||
@ -55,6 +56,7 @@ const routes: Routes = [
|
||||
]
|
||||
},
|
||||
{path: 'contact', component: ContactComponent},
|
||||
{path: 'me/info', component: UserInfoComponent},
|
||||
{path: 'about', component: AboutComponent},
|
||||
{path: 'login', component: LoginComponent},
|
||||
{path: 'logout', component: HomeComponent},
|
||||
|
@ -50,9 +50,11 @@ <h5 class="modal-title alert" id="modal-basic-title">{{'error.modal.title'|trans
|
||||
<div class="collapse navbar-collapse" id="navbarsDefault">
|
||||
<div class="navbar-nav ml-auto">
|
||||
<span *ngIf="auth.authenticated" class="navbar-text border-right pr-2 mr-2">
|
||||
<a class="nav-link" routerLink="/me/info" >
|
||||
{{user.userInfo.full_name}}
|
||||
</a>
|
||||
</span>
|
||||
<ul class="navbar-nav">
|
||||
<ul class="navbar-nav d-lg-flex align-items-center">
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" routerLink="/">
|
||||
<i class="fas fa-home mr-1"></i>{{ 'menu.home' |translate }}
|
||||
|
@ -37,6 +37,8 @@ import {RouterModule} from "@angular/router";
|
||||
import { WithLoadingPipe } from './with-loading.pipe';
|
||||
import { StripLoadingPipe } from './strip-loading.pipe';
|
||||
import { ToastComponent } from './toast/toast.component';
|
||||
import { UserInfoComponent } from './user-info/user-info.component';
|
||||
import {FormsModule} from "@angular/forms";
|
||||
|
||||
export { LoadingValue } from './model/loading-value';
|
||||
export { PageQuery } from './model/page-query';
|
||||
@ -48,7 +50,8 @@ export { PageQuery } from './model/page-query';
|
||||
SortedTableHeaderRowComponent,
|
||||
WithLoadingPipe,
|
||||
StripLoadingPipe,
|
||||
ToastComponent
|
||||
ToastComponent,
|
||||
UserInfoComponent
|
||||
],
|
||||
exports: [
|
||||
CommonModule,
|
||||
@ -84,6 +87,7 @@ export { PageQuery } from './model/page-query';
|
||||
deps: [HttpClient]
|
||||
}
|
||||
}),
|
||||
FormsModule,
|
||||
]
|
||||
})
|
||||
export class SharedModule {
|
||||
|
@ -41,9 +41,7 @@ import {AppNotification} from "@app/model/app-notification";
|
||||
<ng-template #text>{{ toast.body }}</ng-template>
|
||||
</ngb-toast>
|
||||
`,
|
||||
styles: [".ngb-toasts{margin:.5em;padding:0.5em;position:fixed;right:2px;top:20px;z-index:1200}"
|
||||
],
|
||||
host: {'[class.ngb-toasts]': 'true'}
|
||||
styles: [':host { margin:.5em; padding:1em; position:fixed; right:10px; top:40px; z-index:1200; }']
|
||||
})
|
||||
export class ToastComponent implements OnInit {
|
||||
|
||||
|
@ -0,0 +1,87 @@
|
||||
<!--
|
||||
~ Licensed to the Apache Software Foundation (ASF) under one
|
||||
~ or more contributor license agreements. See the NOTICE file
|
||||
~ distributed with this work for additional information
|
||||
~ regarding copyright ownership. The ASF licenses this file
|
||||
~ to you under the Apache License, Version 2.0 (the
|
||||
~ "License"); you may not use this file except in compliance
|
||||
~ with the License. You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~ Unless required by applicable law or agreed to in writing,
|
||||
~ software distributed under the License is distributed on an
|
||||
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
~ KIND, either express or implied. See the License for the
|
||||
~ specific language governing permissions and limitations
|
||||
~ under the License.
|
||||
-->
|
||||
|
||||
<div class="col-md-12">
|
||||
<h2>{{'me.title'|translate}}</h2>
|
||||
<form #passwordForm="ngForm" (ngSubmit)="changePassword()" class="mt-4">
|
||||
<div class="form-group row" *ngFor="let att of ['user_id','full_name','email','language']">
|
||||
<label for="{{att}}" class="col-md-1 col-form-label">{{'users.attributes.' + att|translate}}</label>
|
||||
<div class="col-md-4">
|
||||
<input id="{{att}}" class="form-control" value="{{userService.userInfo[att]}}" readonly>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row"
|
||||
*ngFor="let dateAtt of ['timestamp_account_creation','timestamp_last_password_change']">
|
||||
<label for="{{dateAtt}}" class="col-md-1 col-form-label">{{'users.attributes.' + dateAtt|translate}}</label>
|
||||
<ng-container *ngIf="userService.userInfo[dateAtt]">
|
||||
<div class="col-md-4">
|
||||
<input id="{{dateAtt}}" class="form-control"
|
||||
value="{{userService.userInfo[dateAtt]|date:'YYYY-MM-dd HH:mm:ss'}}" readonly>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-1"> </div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-check" *ngFor="let checkAtt of ['password_change_required','validated','locked']">
|
||||
<input id="{{checkAtt}}" type="checkbox" class="form-check-input disabled"
|
||||
[checked]="userService.userInfo[checkAtt]?'true':null" disabled="disabled">
|
||||
<label for="{{checkAtt}}"
|
||||
class="form-check-label">{{'users.attributes.' + checkAtt|translate}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="mt-3" />
|
||||
<h2>{{'users.edit.changePasswordTitle'|translate}}</h2>
|
||||
<div class="form-group row mt-4" >
|
||||
<label for="current_password" class="col-md-1 col-form-label">{{'users.attributes.current_password'|translate}}</label>
|
||||
<div class="col-md-4">
|
||||
<input id="current_password" name="current_password" type="password" class="form-control" required
|
||||
[(ngModel)]="formData.current_password" #v_current_password="ngModel" [ngClass]="valid('current_password')">
|
||||
<small class="invalid-feedback" *ngIf="v_current_password.errors?.required">{{'form.error.required'|translate}}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" >
|
||||
<label for="password" class="col-md-1 col-form-label">{{'users.attributes.new_password'|translate}}</label>
|
||||
<div class="col-md-4">
|
||||
<input id="password" name="password" type="password" class="form-control" required
|
||||
[ngClass]="valid('password')"
|
||||
[(ngModel)]="formData.password" #v_password="ngModel" >
|
||||
<small class="invalid-feedback" *ngIf="v_password.errors?.required">{{'form.error.required'|translate}}</small>
|
||||
<small class="invalid-feedback" *ngIf="v_password.errors?.invalidpassword">{{v_password.errors.invalidpassword}}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" >
|
||||
<label for="confirm_password" class="col-md-1 col-form-label">{{'users.attributes.confirm_password'|translate}}</label>
|
||||
<div class="col-md-4">
|
||||
<input id="confirm_password" name="confirm_password" type="password" class="form-control" required
|
||||
[(ngModel)]="formData.confirm_password" [class.is-valid]="confirmIsValid()" [class.is-invalid]="confirmIsNotValid()">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" >
|
||||
<div class="col-md-1"> </div>
|
||||
<div class="col-md-4">
|
||||
<button class="btn btn-primary"
|
||||
type="submit">{{'users.edit.submitPassword'|translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
@ -0,0 +1,18 @@
|
||||
/*!
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UserInfoComponent } from './user-info.component';
|
||||
|
||||
describe('UserInfoComponent', () => {
|
||||
let component: UserInfoComponent;
|
||||
let fixture: ComponentFixture<UserInfoComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ UserInfoComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(UserInfoComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import {Component, OnInit, ViewChild} from '@angular/core';
|
||||
import {NgForm} from '@angular/forms';
|
||||
import {UserService} from "@app/services/user.service";
|
||||
import {ToastService} from "@app/services/toast.service";
|
||||
import {ErrorResult} from "@app/model/error-result";
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-info',
|
||||
templateUrl: './user-info.component.html',
|
||||
styleUrls: ['./user-info.component.scss']
|
||||
})
|
||||
export class UserInfoComponent implements OnInit {
|
||||
|
||||
@ViewChild('passwordForm') passwordForm: NgForm;
|
||||
|
||||
formData = {
|
||||
current_password:"",
|
||||
password: "",
|
||||
confirm_password: ""
|
||||
}
|
||||
|
||||
|
||||
constructor(public userService: UserService, private toastService: ToastService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
changePassword() {
|
||||
console.log("Submit Password ")
|
||||
if (!this.formData.current_password || this.formData.current_password.length==0) {
|
||||
this.passwordForm.controls['current_password'].setErrors({'required':'true'})
|
||||
this.passwordForm.controls['current_password'].markAsDirty()
|
||||
}
|
||||
if (!this.formData.password || this.formData.password.length==0) {
|
||||
this.passwordForm.controls['password'].setErrors({'required':'true'})
|
||||
this.passwordForm.controls['password'].markAsDirty()
|
||||
}
|
||||
if (this.passwordForm.valid) {
|
||||
this.userService.changeOwnPassword(this.formData.current_password, this.formData.password, this.formData.confirm_password).subscribe(
|
||||
val => {
|
||||
this.toastService.showSuccessByKey('user-info','users.edit.passwordChanged')
|
||||
this.formData.password=""
|
||||
this.formData.current_password=""
|
||||
this.formData.confirm_password=""
|
||||
this.passwordForm.reset()
|
||||
},
|
||||
( error : ErrorResult)=>{
|
||||
console.log("Error " + error.error_messages[0].message);
|
||||
if (error.error_messages.length>0) {
|
||||
if (error.error_messages[0].error_key.startsWith('user.password.violation')) {
|
||||
this.toastService.showError('user-info', error.error_messages[0].message)
|
||||
this.passwordForm.controls['password'].setErrors({'invalidpassword':error.error_messages[0].message})
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
this.toastService.showErrorByKey('user-info','form.error.invaliddata')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
confirmIsValid() : boolean {
|
||||
return (this.formData.password && this.formData.password.length >0 &&
|
||||
this.formData.confirm_password && this.formData.confirm_password.length>0 && this.formData.password==this.formData.confirm_password )
|
||||
}
|
||||
|
||||
confirmIsNotValid() : boolean {
|
||||
return (this.formData.password && this.formData.password.length>0 &&
|
||||
this.formData.confirm_password && this.formData.confirm_password.length>0 && this.formData.password!=this.formData.confirm_password )
|
||||
}
|
||||
|
||||
valid(field:string) {
|
||||
if (this.passwordForm) {
|
||||
let ctrl = this.passwordForm.controls[field];
|
||||
if (ctrl && ( ctrl.dirty || ctrl.touched ) && ctrl.valid) {
|
||||
return 'is-valid'
|
||||
}
|
||||
if (ctrl && ( ctrl.dirty || ctrl.touched ) && ctrl.invalid) {
|
||||
return 'is-invalid'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -128,6 +128,7 @@ export class ArchivaRequestService {
|
||||
* @param errorMsg the errorMsg as returned by a REST call
|
||||
*/
|
||||
public translateError(errorMsg: ErrorMessage): string {
|
||||
console.log("Translating error "+errorMsg.error_key)
|
||||
if (errorMsg.error_key != null && errorMsg.error_key != '') {
|
||||
let parms = {};
|
||||
if (errorMsg.args != null && errorMsg.args.length > 0) {
|
||||
|
@ -328,7 +328,7 @@ export class UserService implements OnInit, OnDestroy {
|
||||
}), map((httpResponse: HttpResponse<string>) => httpResponse.status == 200));
|
||||
}
|
||||
|
||||
public userRoleTree(userid:String): Observable<RoleTree> {
|
||||
public userRoleTree(userid:string): Observable<RoleTree> {
|
||||
return this.rest.executeResponseCall<RoleTree>("get", "redback","users/"+userid+"/roletree", null).pipe(
|
||||
catchError((error: HttpErrorResponse)=>{
|
||||
if (error.status==404) {
|
||||
@ -341,4 +341,18 @@ export class UserService implements OnInit, OnDestroy {
|
||||
).pipe(map((httpResponse:HttpResponse<RoleTree>)=>httpResponse.body))
|
||||
}
|
||||
|
||||
public changeOwnPassword(current_password:string, password:string, confirm_password:string) {
|
||||
let data = {
|
||||
"user_id":this.userInfo.user_id,
|
||||
"current_password":current_password,
|
||||
"new_password":password,
|
||||
"new_password_confirmation":confirm_password
|
||||
}
|
||||
return this.rest.executeRestCall<any>("post", "redback", "users/me/password/update", data).pipe(
|
||||
catchError((error: HttpErrorResponse)=>{
|
||||
return throwError(this.rest.getTranslatedErrorResult(error));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -78,8 +78,14 @@
|
||||
"created": "Created",
|
||||
"permanent": "Permanent",
|
||||
"last_password_change": "Last Password Change",
|
||||
"timestamp_last_password_change": "Last Password Change",
|
||||
"timestamp_account_creation": "Account Created",
|
||||
"timestamp_last_login": "Last Login",
|
||||
"password": "Password",
|
||||
"confirm_password": "Confirm Password"
|
||||
"new_password": "New Password",
|
||||
"current_password": "Current Password",
|
||||
"confirm_password": "Confirm Password",
|
||||
"language": "UI Language"
|
||||
},
|
||||
"input": {
|
||||
"small": {
|
||||
@ -92,7 +98,6 @@
|
||||
"password": "Enter password",
|
||||
"confirm_password": "Confirm password"
|
||||
},
|
||||
|
||||
"list": {
|
||||
"head": "List Users"
|
||||
},
|
||||
@ -107,7 +112,10 @@
|
||||
"head": "View/Edit User",
|
||||
"small": {
|
||||
"password": "If the password field is empty, it will not be updated."
|
||||
}
|
||||
},
|
||||
"submitPassword": "Change Password",
|
||||
"passwordChanged": "Password has been changed successfully",
|
||||
"changePasswordTitle": "Change password"
|
||||
},
|
||||
"delete": {
|
||||
"head": "Delete User",
|
||||
@ -164,7 +172,6 @@
|
||||
"assignable": "Assignable"
|
||||
}
|
||||
},
|
||||
|
||||
"permissions": {
|
||||
"attributes": {
|
||||
"permission": "Permission",
|
||||
@ -182,7 +189,8 @@
|
||||
"error": {
|
||||
"required": "Value is empty. This is required.",
|
||||
"containsWhitespace": "Value must not contain whitespace.",
|
||||
"userexists": "This user exists already."
|
||||
"userexists": "This user exists already.",
|
||||
"invaliddata": "The data is not valid"
|
||||
},
|
||||
"button": {
|
||||
"yes": "Yes",
|
||||
@ -198,7 +206,9 @@
|
||||
},
|
||||
"password": {
|
||||
"violations": {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
"me": {
|
||||
"title": "Current User Information"
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user