diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/error-result.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/error-result.ts index 64e97659f..b54fa848f 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/error-result.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/error-result.ts @@ -20,6 +20,7 @@ import {ErrorMessage} from "./error-message"; export class ErrorResult { error_messages: Array + status: number; constructor(errorMessages: Array) { this.error_messages = errorMessages; diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user.spec.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user.spec.ts new file mode 100644 index 000000000..40e143798 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user.spec.ts @@ -0,0 +1,25 @@ +/* + * 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 { User } from './user'; + +describe('User', () => { + it('should create an instance', () => { + expect(new User()).toBeTruthy(); + }); +}); diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user.ts new file mode 100644 index 000000000..f264d0218 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user.ts @@ -0,0 +1,24 @@ +/* + * 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 { UserInfo } from './user-info'; + +export class User extends UserInfo { + password: string; + confirm_password: string; +} diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/manage-users/manage-users.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/manage-users/manage-users.component.html index c61718009..08b3fca81 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/manage-users/manage-users.component.html +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/manage-users/manage-users.component.html @@ -24,6 +24,9 @@ + \ No newline at end of file diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.html index 1bfac6606..ad538a838 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.html +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.html @@ -18,47 +18,67 @@ -->
-
-
- - - {{'users.input.small.user_id'|translate}} +
+ + + {{'users.input.small.user_id'|translate:{'minSize':this.minUserIdSize} }} +
+
+ + + {{'users.input.small.full_name'|translate}} +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
-
- - - {{'users.input.small.full_name'|translate}} +
+ +
-
- - -
-
-
- - -
-
- - -
-
-
+
+
-
-
+ + +
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.ts index ce95bff62..6a50028b7 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.ts @@ -18,8 +18,14 @@ */ import {Component, OnInit} from '@angular/core'; -import {FormControl, FormGroup, Validators, FormBuilder} from '@angular/forms'; +import {Validators, FormBuilder, FormGroup} from '@angular/forms'; import {UserService} from "../../../../services/user.service"; +import {User} from "../../../../model/user"; +import { UserInfo } from 'src/app/model/user-info'; +import {HttpErrorResponse} from "@angular/common/http"; +import {ErrorResult} from "../../../../model/error-result"; +import {catchError} from "rxjs/operators"; +import {of, throwError} from 'rxjs'; @Component({ selector: 'app-manage-users-add', @@ -28,12 +34,23 @@ import {UserService} from "../../../../services/user.service"; }) export class ManageUsersAddComponent implements OnInit { + minUserIdSize=8; + success:boolean=false; + error:boolean=false; + errorResult:ErrorResult; + result:string; + userid:string; + userForm = this.fb.group({ - userId: ['', [Validators.required, Validators.minLength(8)]], - fullName: ['', Validators.required], + user_id: ['', [Validators.required, Validators.minLength(this.minUserIdSize)]], + full_name: ['', Validators.required], email: ['', [Validators.required,Validators.email]], locked: [false], - passwordChangeRequired: [true] + password_change_required: [true], + password: [''], + confirm_password: [''], + }, { + validator: MustMatch('password', 'confirm_password') }) constructor(private userService: UserService, private fb: FormBuilder) { @@ -45,13 +62,41 @@ export class ManageUsersAddComponent implements OnInit { onSubmit() { // Process checkout data here - console.warn('Your order has been submitted', JSON.stringify(this.userForm.value)); + this.result=null; + if (this.userForm.valid) { + let user = this.copyForm(['user_id','full_name','email','locked','password_change_required', + 'password','confirm_password']) + console.info('Adding user ' + user); + this.userService.addUser(user).pipe(catchError((error : ErrorResult)=> { + console.log("Error " + error + " - " + typeof (error) + " - " + JSON.stringify(error)); + if (error.status==422) { + console.warn("Validation error"); + + } + this.errorResult = error; + this.success=false; + this.error=true; + return throwError(error); + })).subscribe((location : string ) => { + this.result = location; + this.success=true; + this.error = false; + this.userid = location.substring(location.lastIndexOf('/') + 1); + }); + } } - get userId() { - return this.userForm.get('userId'); + + private copyForm(properties:string[]) : User { + let user : any = new User(); + for (let prop of properties) { + user[prop] = this.userForm.get(prop).value; + } + console.log("User " + user); + return user; } + valid(field:string) : string { let formField = this.userForm.get(field); if (formField.dirty||formField.touched) { @@ -65,4 +110,26 @@ export class ManageUsersAddComponent implements OnInit { } } + + + } + +export function MustMatch(controlName: string, matchingControlName: string) { + return (formGroup: FormGroup) => { + const control = formGroup.controls[controlName]; + const matchingControl = formGroup.controls[matchingControlName]; + + if (matchingControl.errors && !matchingControl.errors.mustMatch) { + // return if another validator has already found an error on the matchingControl + return; + } + + // set error on matchingControl if validation fails + if (control.value !== matchingControl.value) { + matchingControl.setErrors({ mustMatch: true }); + } else { + matchingControl.setErrors(null); + } + } +} \ No newline at end of file diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-list/manage-users-list.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-list/manage-users-list.component.html index fbd689508..960e16d3a 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-list/manage-users-list.component.html +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-list/manage-users-list.component.html @@ -50,7 +50,7 @@ [ngClass]="user.validated?'fa-check-circle':'fa-circle'"> - {{user.timestamp_last_login | date:'yyyy-MM-ddTHH:mm:ss'}} {{user.timestamp_account_creation | date : 'yyyy-MM-ddTHH:mm:ss'}} diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/archiva-request.service.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/archiva-request.service.ts index 0227893c1..2f1e9939f 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/archiva-request.service.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/archiva-request.service.ts @@ -17,11 +17,12 @@ * under the License. */ import {Injectable} from '@angular/core'; -import {HttpClient} from "@angular/common/http"; +import {HttpClient, HttpErrorResponse, HttpResponse} from "@angular/common/http"; import {environment} from "../../environments/environment"; import {Observable} from "rxjs"; import {ErrorMessage} from "../model/error-message"; import {TranslateService} from "@ngx-translate/core"; +import {ErrorResult} from "../model/error-result"; @Injectable({ providedIn: 'root' @@ -42,6 +43,21 @@ export class ArchivaRequestService { * @param input the input data, if this is a POST or UPDATE request */ executeRestCall(type: string, module: string, service: string, input: object): Observable { + let httpArgs = this.getHttpOptions(type, module, service, input); + httpArgs['options']['observe'] = 'body'; + httpArgs['options']['responseType'] = 'json'; + + let lType = type.toLowerCase(); + if (lType == "get") { + return this.http.get(httpArgs.url, httpArgs.options); + } else if (lType == "head" ) { + return this.http.head(httpArgs.url, httpArgs.options); + } else if (lType == "post") { + return this.http.post(httpArgs.url, input, httpArgs.options); + } + } + + private getHttpOptions(type: string, module: string, service: string, input: object) { let modulePath = environment.application.servicePaths[module]; let url = environment.application.baseUrl + environment.application.restPath + "/" + modulePath + "/" + service; let token = this.getToken(); @@ -53,14 +69,28 @@ export class ArchivaRequestService { } else { headers = {}; } - if (type == "get") { + let options = {'headers': headers} + if (type.toLowerCase()=='get') { let params = {} if (input!=null) { params = input; } - return this.http.get(url, {"headers": headers,"params":params}); - } else if (type == "post") { - return this.http.post(url, input, {"headers": headers}); + options['params'] = params; + } + return {'url':url, 'options':options} + } + + executeResponseCall(type: string, module: string, service:string, input:object) : Observable> { + let httpArgs = this.getHttpOptions(type, module, service, input); + httpArgs['options']['observe'] = 'response'; + httpArgs['options']['responseType'] = 'json'; + let lType = type.toLowerCase(); + if (lType == "get") { + return this.http.get>(httpArgs.url, httpArgs.options); + } else if (lType=='head') { + return this.http.head>(httpArgs.url, httpArgs.options); + } else if (lType == 'post') { + return this.http.post>(httpArgs.url, input, httpArgs.options); } } @@ -97,4 +127,17 @@ export class ArchivaRequestService { return this.translator.instant('api.' + errorMsg.error_key, parms); } } + + public getTranslatedErrorResult(httpError : HttpErrorResponse) : ErrorResult { + let errorResult = httpError.error as ErrorResult; + errorResult.status = httpError.status; + if (errorResult.error_messages!=null) { + for (let message of errorResult.error_messages) { + if (message.message==null || message.message=='') { + message.message = this.translateError(message); + } + } + } + return errorResult; + } } diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts index 1792ec805..d3b8e9a87 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts @@ -19,12 +19,14 @@ import {Injectable, OnDestroy, OnInit} from '@angular/core'; import {ArchivaRequestService} from "./archiva-request.service"; import {UserInfo} from '../model/user-info'; -import {HttpErrorResponse} from "@angular/common/http"; +import {HttpErrorResponse, HttpResponse} from "@angular/common/http"; import {ErrorResult} from "../model/error-result"; -import {Observable} from "rxjs"; +import {Observable, throwError} from "rxjs"; import {Permission} from '../model/permission'; import {PagedResult} from "../model/paged-result"; import {EntityService} from "../model/entity-service"; +import { User } from '../model/user'; +import {catchError, map} from "rxjs/operators"; @Injectable({ providedIn: 'root' @@ -269,4 +271,11 @@ export class UserService implements OnInit, OnDestroy { return this.rest.executeRestCall>("get", "redback", "users", {'q':searchTerm, 'offset':offset,'limit':limit,'orderBy':orderBy,'order':order}); } + + public addUser(user : User) : Observable { + return this.rest.executeResponseCall("post", "redback", "users", user).pipe( + catchError( ( error: HttpErrorResponse) => { + return throwError(this.rest.getTranslatedErrorResult(error)); + }),map( (httpResponse : HttpResponse) => httpResponse.headers.get('Location'))); + } } diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json index 02a32fd84..05843c492 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json @@ -47,7 +47,8 @@ } }, "api" : { - "rb.auth.invalid_credentials": "Invalid credentials given" + "rb.auth.invalid_credentials": "Invalid credentials given", + "user.password.violation.numeric" : "Password must have at least {{arg0}} numeric characters." }, "users": { "attributes":{ @@ -61,16 +62,20 @@ "last_login": "Last Login", "created": "Created", "permanent": "Permanent", - "last_password_change": "Last Password Change" + "last_password_change": "Last Password Change", + "password": "Password", + "confirm_password": "Confirm Password" }, "input" : { "small": { - "user_id": "Must be a unique key. No space allowed.", + "user_id": "Must be a unique key with at least {{minSize}} characters. No spaces allowed.", "full_name": "This is the display name of the user" }, "user_id": "Enter user ID", "full_name": "Enter full user name", - "email": "email@example.org" + "email": "email@example.org", + "password": "Enter password", + "confirm_password": "Confirm password" }, "list": { @@ -88,5 +93,10 @@ }, "form": { "submit": "Submit" + }, + "password": { + "violations" : { + + } } } \ No newline at end of file