[DEV] Add and delete application token

This commit is contained in:
Edouard DUPIN 2023-02-03 00:44:58 +01:00
parent 7abf7f5674
commit a2e1f5b24d
7 changed files with 126 additions and 34 deletions

View File

@ -52,7 +52,7 @@ public class ApplicationTokenResource {
int nbRemoved = SqlWrapper.setDeleteWhere(ApplicationToken.class, int nbRemoved = SqlWrapper.setDeleteWhere(ApplicationToken.class,
List.of( List.of(
new WhereCondition("parentId", "=", applicationId), new WhereCondition("parentId", "=", applicationId),
new WhereCondition("tokenId", "=", tokenId) new WhereCondition("id", "=", tokenId)
) )
); );
if (nbRemoved == 0) { if (nbRemoved == 0) {
@ -79,7 +79,7 @@ public class ApplicationTokenResource {
static String randomToken() { static String randomToken() {
int len = 48; int len = 48;
String valid_element = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvxyz0123456789#_@-|[])('~$%*/\\.!?,"; String valid_element = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvxyz0123456789#_@-~*!?";
// creating a StringBuffer size of AlphaNumericStr // creating a StringBuffer size of AlphaNumericStr
StringBuilder out = new StringBuilder(len); StringBuilder out = new StringBuilder(len);
int iii; int iii;
@ -91,24 +91,24 @@ public class ApplicationTokenResource {
} }
return out.toString(); return out.toString();
} }
public record CreateRequest(String name, Integer validity) {};
@POST @POST
@Path("/{applicationId}/create") @Path("/{applicationId}/create")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
public ApplicationToken createToken( public ApplicationToken createToken(
@Context SecurityContext sc, @Context SecurityContext sc,
@PathParam("applicationId") Long applicationId, @PathParam("applicationId") Long applicationId,
@FormDataParam("name") String name, CreateRequest request
@FormDataParam("validity") Integer validity
) throws Exception { ) throws Exception {
// correct input string stream : // correct input string stream :
name = multipartCorrection(name); String name = multipartCorrection(request.name());
//validity = multipartCorrection(validity); //validity = multipartCorrection(validity);
System.out.println("create a nexw token..."); System.out.println("create a nexw token...");
if (applicationId == null) { if (applicationId == null) {
throw new InputException("applicationId", "can not be null"); throw new InputException("applicationId", "can not be null");
} }
int maximum = 365*5; int maximum = 365*5;
Integer validity = request.validity();
if (validity == null || validity < 0 || validity > maximum) { if (validity == null || validity < 0 || validity > maximum) {
validity = maximum; validity = maximum;
} }
@ -126,7 +126,10 @@ public class ApplicationTokenResource {
// here we return the token to permit to the user to see it to set it in the application. // here we return the token to permit to the user to see it to set it in the application.
return token; return token;
} }
/*
Cannot find a deserializer for non-concrete Map type [map type; class jakarta.ws.rs.core.MultivaluedMap, [simple type, class java.lang.String] -> [collection type; class java.util.List, contains [simple type, class java.lang.String]]]
at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 1, column: 1]
*/
} }

View File

@ -8,13 +8,13 @@
<table width="100%"> <table width="100%">
<tr> <tr>
<td width="25%"><b>id:</b></td> <td width="25%"><b>id:</b></td>
<td width="75%">{{application.id}}</td> <td width="75%">{{application?.id}}</td>
</tr> </tr>
<tr> <tr>
<td><b>Name:</b></td> <td><b>Name:</b></td>
<td> <td>
<app-entry <app-entry
[value]="application.name" [value]="application?.name"
placeholder="Enter application name" placeholder="Enter application name"
[hasError]="nameState !== true" [hasError]="nameState !== true"
(changeValue)="checkName($event)"></app-entry> (changeValue)="checkName($event)"></app-entry>
@ -25,7 +25,7 @@
<td><b>Description:</b></td> <td><b>Description:</b></td>
<td> <td>
<app-entry <app-entry
[value]="application.description" [value]="application?.description"
(changeValue)="updateDescription($event)"></app-entry> (changeValue)="updateDescription($event)"></app-entry>
</td> </td>
</tr> </tr>
@ -33,7 +33,7 @@
<td><b>Redirect (http://):</b></td> <td><b>Redirect (http://):</b></td>
<td> <td>
<app-entry <app-entry
[value]="application.redirect" [value]="application?.redirect"
placeholder="Enter http redirect adresses " placeholder="Enter http redirect adresses "
[hasError]="redirectState !== true" [hasError]="redirectState !== true"
(changeValue)="checkRedirect($event)"></app-entry> (changeValue)="checkRedirect($event)"></app-entry>
@ -44,7 +44,7 @@
<td><b>Redirect development (http://):</b></td> <td><b>Redirect development (http://):</b></td>
<td> <td>
<app-entry <app-entry
[value]="application.redirectDev" [value]="application?.redirectDev"
(changeValue)="updateRedirectDev($event)"></app-entry> (changeValue)="updateRedirectDev($event)"></app-entry>
</td> </td>
</tr> </tr>
@ -52,7 +52,7 @@
<td><b>Notification address (http://):</b></td> <td><b>Notification address (http://):</b></td>
<td> <td>
<app-entry <app-entry
[value]="application.notification" [value]="application?.notification"
(changeValue)="updateNotification($event)"></app-entry> (changeValue)="updateNotification($event)"></app-entry>
</td> </td>
</tr> </tr>
@ -60,7 +60,7 @@
<td><b>TTL (seconds before expiration):</b></td> <td><b>TTL (seconds before expiration):</b></td>
<td> <td>
<app-entry <app-entry
[value]="application.ttl" [value]="application?.ttl"
(changeValue)="updateTTL($event)"></app-entry> (changeValue)="updateTTL($event)"></app-entry>
</td> </td>
</tr> </tr>
@ -86,20 +86,37 @@
<body> <body>
<table class="table-model"> <table class="table-model">
<tr> <tr>
<th width="5%">id</th> <th>id</th>
<th width="25%">Name</th> <th>Name</th>
<th width="30%">Expiration Time</th> <th>Expiration Time</th>
<th width="40%">Token</th> <th>Token</th>
<th>Actions</th>
</tr> </tr>
<tr *ngFor="let token of tokens"> <tr *ngFor="let token of tokens">
<td>{{token.id}}</td> <td>{{token.id}}</td>
<td>{{token.name}}</td> <td>{{token.name}}</td>
<td>{{formatTimestamp(user.endValidityTime)}}</td> <td>{{formatTimestamp(token.endValidityTime)}}</td>
<td>{{token.token}}</td> <td>{{token.token}}</td>
<td>
<button
class="square-button login color-button-cancel color-shadow-black"
(click)="onRemoveApplicationToken($event, token)"
type="submit">
<i class="material-icons">delete_forever</i>
</button>
</td>
</tr> </tr>
</table> </table>
</body> </body>
<footer> <footer>
<app-entry
[value]="tokenName"
placeholder="Enter the token name / decription"
(changeValue)="checkTokenName($event)"></app-entry>
<app-entry
[value]="tokenTTL"
placeholder="Enter the Time To Lead"
(changeValue)="checkTokenTTL($event)"></app-entry>
<button <button
class="button login color-button-validate color-shadow-black" class="button login color-button-validate color-shadow-black"
id="create-button" id="create-button"
@ -112,7 +129,7 @@
</div> </div>
<div class="clear"></div> <div class="clear"></div>
</div> </div>
<!--
<delete-confirm <delete-confirm
[comment]="confirmDeleteComment" [comment]="confirmDeleteComment"
(callback)="deleteConfirmed()"></delete-confirm>--> (callback)="deleteConfirmed()"></delete-confirm>

View File

@ -12,3 +12,21 @@
text-transform: uppercase; text-transform: uppercase;
font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif; font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif;
} }
.table-model {
tr:nth-child(odd) {
background-color: rgb(180, 180, 180);
//color: #fff;
}
th {
background-color: darkgray;
text-align: center;
}
td {
text-align: center;
}
}
table {
width: 100%;
}

View File

@ -4,11 +4,12 @@
* @license PROPRIETARY (see license file) * @license PROPRIETARY (see license file)
*/ */
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { ApplicationService, ApplicationModel, ApplicationTokenService } from 'app/service'; import { ApplicationService, ApplicationModel, ApplicationTokenService } from 'app/service';
import { ApplicationTokenModel } from 'app/service/application-token'; import { ApplicationTokenModel } from 'app/service/application-token';
import { AsyncActionState } from 'common/component'; import { AsyncActionState } from 'common/component';
import { NotificationService, PopInService } from 'common/service';
import { isNumeric } from 'common/utils'; import { isNumeric } from 'common/utils';
@Component({ @Component({
@ -21,12 +22,15 @@ export class ApplicationEditScene implements OnInit {
id: number = undefined id: number = undefined
application: ApplicationModel = undefined; application: ApplicationModel = undefined;
applicationRef: ApplicationModel = undefined; applicationRef: ApplicationModel = undefined;
tokens: any = undefined; tokens: ApplicationTokenModel[] = [];
constructor( constructor(
private applicationService: ApplicationService, private applicationService: ApplicationService,
private applicationTokenService: ApplicationTokenService, private applicationTokenService: ApplicationTokenService,
private activatedRoute: ActivatedRoute, private activatedRoute: ActivatedRoute,
private cdr: ChangeDetectorRef,
private popInService: PopInService,
private notificationService: NotificationService,
) { ) {
} }
ngOnInit() { ngOnInit() {
@ -46,7 +50,7 @@ export class ApplicationEditScene implements OnInit {
}); });
this.applicationTokenService this.applicationTokenService
.gets(this.id) .gets(this.id)
.then((response: ApplicationTokenModel) => { .then((response: ApplicationTokenModel[]) => {
console.log(`??? get full response: ${JSON.stringify(response, null, 4)}`); console.log(`??? get full response: ${JSON.stringify(response, null, 4)}`);
self.tokens = response; self.tokens = response;
}) })
@ -55,7 +59,9 @@ export class ApplicationEditScene implements OnInit {
}); });
} }
formatTimestamp(unix_timestamp: number) {
return new Date(unix_timestamp).toISOString().replace("T", " ").replace("Z", " GMT").replace(".000", "");
}
updateDescription(newValue: string): void { updateDescription(newValue: string): void {
this.application.description = newValue; this.application.description = newValue;
@ -122,7 +128,7 @@ export class ApplicationEditScene implements OnInit {
*/ */
checkName(newValue: string): void { checkName(newValue: string): void {
this.application.name = newValue; this.application.name = newValue;
if (this.application.name.length < 6) { if (this.application?.name?.length < 6) {
this.nameState = "This name is too small >=6."; this.nameState = "This name is too small >=6.";
} }
this.nameState = true; this.nameState = true;
@ -134,7 +140,7 @@ export class ApplicationEditScene implements OnInit {
checkRedirect(newValue: string): void { checkRedirect(newValue: string): void {
this.application.redirect = newValue; this.application.redirect = newValue;
this.redirectState = true; this.redirectState = true;
if (this.application.redirect.length <= 5) { if (this.application?.redirect?.length <= 5) {
this.redirectState = "This redirect is too small."; this.redirectState = "This redirect is too small.";
} }
this.updateButtonVisibility(); this.updateButtonVisibility();
@ -175,15 +181,59 @@ export class ApplicationEditScene implements OnInit {
createToken(): void { createToken(): void {
let self = this; let self = this;
this.applicationTokenService this.applicationTokenService
.create(this.id, "plpolp", 953) .create(this.id, this.tokenName, this.tokenTTL)
.then((response: ApplicationTokenModel) => { .then((response: ApplicationTokenModel) => {
console.log(`??? get full response: ${JSON.stringify(response, null, 4)}`); console.log(`??? get fullllllll response: ${JSON.stringify(response, null, 4)}`);
self.tokens.append(response); self.tokens.push(response);
self.cdr.detectChanges();
}) })
.catch((error: any) => { .catch((error: any) => {
console.log(`??? get ERROR response: ${JSON.stringify(error, null, 4)}`); console.log(`??? get ERROR response: ${JSON.stringify(error, null, 4)}`);
}); });
} }
onRemoveApplicationToken(_event: any, token: ApplicationTokenModel) {
this.confirmDeleteComment = `Delete the application token ID: [${this.application.id}/${token.id}] ${token.name}`;
this.confirmDeleteApplicationToken = token;
this.popInService.open('popin-delete-confirm');
}
removeApplicationConfirm(token: ApplicationTokenModel) {
let self = this;
this.applicationTokenService.remove(self.application.id, token.id)
.then(
() => {
const index = self.tokens.indexOf(token, 0);
if (index > -1) {
self.tokens.splice(index, 1);
self.cdr.detectChanges();
}
}
).catch(
(error: any) => {
self.notificationService.errorRaw(`Fail to delete application token: [${self.application.id}/${token.id}] : ${token.name} ==> ${error}`)
}
);
}
confirmDeleteComment: string = undefined;
confirmDeleteApplicationToken: ApplicationTokenModel = undefined;
deleteConfirmed() {
if(this.confirmDeleteApplicationToken !== undefined) {
this.removeApplicationConfirm(this.confirmDeleteApplicationToken);
this.confirmDeleteComment = undefined;
this.confirmDeleteApplicationToken = undefined;
}
}
tokenName: string = undefined;
tokenTTL: number = undefined;
checkTokenName(newValue: string) {
this.tokenName = newValue;
}
checkTokenTTL(newValue: string) {
if (isNumeric(newValue)) {
this.tokenTTL = Number(newValue);
}
}
} }

View File

@ -27,7 +27,7 @@ export class ApplicationTokenService {
console.log('Start ApplicationTokenService'); console.log('Start ApplicationTokenService');
} }
gets(applicationId: number): Promise<ApplicationTokenModel> { gets(applicationId: number): Promise<ApplicationTokenModel[]> {
let self = this; let self = this;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.http this.http

View File

@ -4,6 +4,6 @@
[placeholder]="placeholder" [placeholder]="placeholder"
required="" required=""
[style.border]="hasError? '2px dashed red' : ''" [style.border]="hasError? '2px dashed red' : ''"
[value]="value" [value]="value===undefined?'':value"
(input)="onChangeValue($event.target.value)" /> (input)="onChangeValue($event.target.value)" />
</div> </div>

View File

@ -3,14 +3,14 @@
* @copyright 2018, Edouard DUPIN, all right reserved * @copyright 2018, Edouard DUPIN, all right reserved
* @license PROPRIETARY (see license file) * @license PROPRIETARY (see license file)
*/ */
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
@Component({ @Component({
selector: 'app-entry', selector: 'app-entry',
templateUrl: 'entry.html', templateUrl: 'entry.html',
styleUrls: ['entry.less'], styleUrls: ['entry.less'],
}) })
export class EntryComponent { export class EntryComponent implements OnInit {
/// Value of the password /// Value of the password
@Input() value: string = ''; @Input() value: string = '';
/// Placeholder of the Value /// Placeholder of the Value
@ -20,6 +20,10 @@ export class EntryComponent {
/// event when change the value of the password /// event when change the value of the password
@Output() changeValue: EventEmitter<string> = new EventEmitter(); @Output() changeValue: EventEmitter<string> = new EventEmitter();
ngOnInit(): void {
//if (value)
}
/** /**
* When input value change, need update the display and change the internal value. * When input value change, need update the display and change the internal value.
* @param newValue New value set on the password * @param newValue New value set on the password