TSK-714 - Improve all workbasket access items for a user screen

This commit is contained in:
Jose Ignacio Recuerda Cambil 2018-09-18 16:46:55 +02:00 committed by Holger Hagen
parent b7aee393ae
commit 3d1a921394
14 changed files with 138 additions and 48 deletions

View File

@ -288,8 +288,8 @@ public class LdapCacheTestImpl implements LdapCache {
new AccessIdResource("team_4", "cn=team_4" + ",ou=groups,o=TaskanaTest"))); new AccessIdResource("team_4", "cn=team_4" + ",ou=groups,o=TaskanaTest")));
@Override @Override
public List<AccessIdResource> findMatchingAccessId(String searchFor, int maxNumerOfReturnedAccessIds) { public List<AccessIdResource> findMatchingAccessId(String searchFor, int maxNumberOfReturnedAccessIds) {
return findAccessIdResource(searchFor, maxNumerOfReturnedAccessIds, false); return findAcessIdResource(searchFor, maxNumberOfReturnedAccessIds, false);
} }
@Override @Override
@ -297,10 +297,17 @@ public class LdapCacheTestImpl implements LdapCache {
if (users == null) { if (users == null) {
addUsersToGroups(); addUsersToGroups();
} }
return findAccessIdResource(searchFor, maxNumberOfReturnedAccessIds, true); return findAcessIdResource(searchFor, maxNumberOfReturnedAccessIds, true);
} }
private List<AccessIdResource> findAccessIdResource(String searchFor, int maxNumerOfReturnedAccessIds, @Override
public List<AccessIdResource> validateAccessId(String accessId) {
return accessIds.stream()
.filter(t -> (t.getAccessId().equalsIgnoreCase(accessId.toLowerCase())))
.collect(Collectors.toList());
}
private List<AccessIdResource> findAcessIdResource(String searchFor, int maxNumberOfReturnedAccessIds,
boolean groupMember) { boolean groupMember) {
List<AccessIdResource> usersAndGroups = accessIds.stream() List<AccessIdResource> usersAndGroups = accessIds.stream()
.filter(t -> (t.getName().toLowerCase().contains(searchFor.toLowerCase()) .filter(t -> (t.getName().toLowerCase().contains(searchFor.toLowerCase())
@ -321,7 +328,7 @@ public class LdapCacheTestImpl implements LdapCache {
}); });
List<AccessIdResource> result = usersAndGroups.subList(0, List<AccessIdResource> result = usersAndGroups.subList(0,
Math.min(usersAndGroups.size(), maxNumerOfReturnedAccessIds)); Math.min(usersAndGroups.size(), maxNumberOfReturnedAccessIds));
return result; return result;
} }

View File

@ -51,7 +51,7 @@ public class AccessIdValidationControllerTest {
headers.add("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x"); headers.add("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x");
HttpEntity<String> request = new HttpEntity<String>(headers); HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<List<AccessIdResource>> response = template.exchange( ResponseEntity<List<AccessIdResource>> response = template.exchange(
"http://127.0.0.1:" + port + "/v1/access-ids?searchFor=ali", HttpMethod.GET, request, "http://127.0.0.1:" + port + "/v1/access-ids?search-for=ali", HttpMethod.GET, request,
new ParameterizedTypeReference<List<AccessIdResource>>() { new ParameterizedTypeReference<List<AccessIdResource>>() {
}); });
@ -73,7 +73,7 @@ public class AccessIdValidationControllerTest {
HttpEntity<String> request = new HttpEntity<String>(headers); HttpEntity<String> request = new HttpEntity<String>(headers);
try { try {
template.exchange( template.exchange(
"http://127.0.0.1:" + port + "/v1/access-ids?searchFor=al", HttpMethod.GET, request, "http://127.0.0.1:" + port + "/v1/access-ids?search-for=al", HttpMethod.GET, request,
new ParameterizedTypeReference<List<AccessIdResource>>() { new ParameterizedTypeReference<List<AccessIdResource>>() {
}); });

View File

@ -90,7 +90,7 @@ public class GenenalExceptionHandlingTest {
AccessIdController.setLdapCache(new LdapCacheTestImpl()); AccessIdController.setLdapCache(new LdapCacheTestImpl());
template.exchange( template.exchange(
server + port + "/v1/access-ids?searchFor=al", HttpMethod.GET, request, server + port + "/v1/access-ids?search-for=al", HttpMethod.GET, request,
new ParameterizedTypeReference<List<AccessIdResource>>() { new ParameterizedTypeReference<List<AccessIdResource>>() {
}); });

View File

@ -16,17 +16,24 @@ public interface LdapCache {
* *
* @param searchFor * @param searchFor
* the search string. The search is performed over names and ids of users and groups. * the search string. The search is performed over names and ids of users and groups.
* @param maxNumerOfReturnedAccessIds * @param maxNumberOfReturnedAccessIds
* the maximum number of results to return. * the maximum number of results to return.
* @return a List of access ids for users and group where the name or id contains the search string. * @return a List of access ids for users and group where the name or id contains the search string.
*/ */
List<AccessIdResource> findMatchingAccessId(String searchFor, int maxNumerOfReturnedAccessIds); List<AccessIdResource> findMatchingAccessId(String searchFor, int maxNumberOfReturnedAccessIds);
/** /**
* * Find the groups belong to a user.
* @param searchFor the search string. The search is performed over names and ids of group . * @param searchFor the search string. The search is performed over names and ids of group .
* @param maxNumerOfReturnedAccessIds * @param maxNumberOfReturnedAccessIds the maximum number of results to return.
* @return * @return
*/ */
List<AccessIdResource> findGroupsOfUser(String searchFor, int maxNumerOfReturnedAccessIds); List<AccessIdResource> findGroupsOfUser(String searchFor, int maxNumberOfReturnedAccessIds);
/**
* Validate a access id.
* @param accessId the search string.
* @return the corresponding access id.
*/
List<AccessIdResource> validateAccessId(String accessId);
} }

View File

@ -35,28 +35,40 @@ public class AccessIdController {
@GetMapping @GetMapping
public ResponseEntity<List<AccessIdResource>> validateAccessIds( public ResponseEntity<List<AccessIdResource>> validateAccessIds(
@RequestParam String searchFor, @RequestParam(required = false) boolean searchInGroups) throws InvalidArgumentException { @RequestParam("search-for") String searchFor) throws InvalidArgumentException {
if (searchFor.length() < ldapClient.getMinSearchForLength()) { if (searchFor.length() < ldapClient.getMinSearchForLength()) {
throw new InvalidArgumentException("searchFor string '" + searchFor + "' is too short. Minimum searchFor length = " throw new InvalidArgumentException(
"searchFor string '" + searchFor + "' is too short. Minimum searchFor length = "
+ ldapClient.getMinSearchForLength()); + ldapClient.getMinSearchForLength());
} }
if (ldapClient.useLdap()) { if (ldapClient.useLdap()) {
List<AccessIdResource> accessIdUsers = ldapClient.searchUsersAndGroups(searchFor); List<AccessIdResource> accessIdUsers = ldapClient.searchUsersAndGroups(searchFor);
if (searchInGroups) {
List<AccessIdResource> accessIdGroups = ldapClient.searchGroupsofUsersIsMember(searchFor);
accessIdUsers.addAll(accessIdGroups);
}
return new ResponseEntity<>(accessIdUsers, HttpStatus.OK); return new ResponseEntity<>(accessIdUsers, HttpStatus.OK);
} else if (ldapCache != null) { } else if (ldapCache != null) {
if (searchInGroups) {
return new ResponseEntity<>(
ldapCache.findGroupsOfUser(searchFor, ldapClient.getMaxNumberOfReturnedAccessIds()),
HttpStatus.OK);
} else {
return new ResponseEntity<>( return new ResponseEntity<>(
ldapCache.findMatchingAccessId(searchFor, ldapClient.getMaxNumberOfReturnedAccessIds()), ldapCache.findMatchingAccessId(searchFor, ldapClient.getMaxNumberOfReturnedAccessIds()),
HttpStatus.OK); HttpStatus.OK);
} else {
return new ResponseEntity<>(new ArrayList<>(), HttpStatus.NOT_FOUND);
} }
}
@GetMapping(path = "/groups")
public ResponseEntity<List<AccessIdResource>> getGroupsByAccessId(
@RequestParam("access-id") String accessId) throws InvalidArgumentException {
if (ldapClient.useLdap() || ldapCache != null) {
if (!validateAccessId(accessId)) {
throw new InvalidArgumentException("The accessId is invalid");
}
}
List<AccessIdResource> accessIdUsers;
if (ldapClient.useLdap()) {
accessIdUsers = ldapClient.searchUsersAndGroups(accessId);
accessIdUsers.addAll(ldapClient.searchGroupsofUsersIsMember(accessId));
return new ResponseEntity<>(accessIdUsers, HttpStatus.OK);
} else if (ldapCache != null) {
accessIdUsers = ldapCache.findGroupsOfUser(accessId, ldapClient.getMaxNumberOfReturnedAccessIds());
return new ResponseEntity<>(accessIdUsers, HttpStatus.OK);
} else { } else {
return new ResponseEntity<>(new ArrayList<>(), HttpStatus.NOT_FOUND); return new ResponseEntity<>(new ArrayList<>(), HttpStatus.NOT_FOUND);
} }
@ -66,4 +78,9 @@ public class AccessIdController {
ldapCache = cache; ldapCache = cache;
} }
private boolean validateAccessId(String accessId) throws InvalidArgumentException {
return (ldapClient.useLdap() && ldapClient.searchUsersAndGroups(accessId).size() == 1) || (!ldapClient.useLdap()
&& ldapCache.validateAccessId(accessId).size() == 1);
}
} }

View File

@ -1,10 +1,5 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<div class="pull-right btn-group">
<button *ngIf="AccessItemsForm" type="button" (click)="revokeAccess()" class="btn btn-default" data-toggle="tooltip" title="Revoke access">
<span class="glyphicon glyphicon-remove red" aria-hidden="true"></span>
</button>
</div>
<h4 class="panel-header">Acces items management</h4> <h4 class="panel-header">Acces items management</h4>
</div> </div>
<div class="panel-body"> <div class="panel-body">
@ -50,7 +45,8 @@
<th class="text-align"><input type="text" formControlName="accessIdFilter" (keyup.enter)="searchForAccessItemsWorkbaskets()" <th class="text-align"><input type="text" formControlName="accessIdFilter" (keyup.enter)="searchForAccessItemsWorkbaskets()"
class="form-control" placeholder="Access id filter"></th> class="form-control" placeholder="Access id filter"></th>
<th> <th>
<button type="button" (click)="searchForAccessItemsWorkbaskets()" class="btn btn-default" data-toggle="tooltip" title="Search"> <button type="button" (click)="searchForAccessItemsWorkbaskets()" class="btn btn-default" data-toggle="tooltip"
title="Search">
<span class="glyphicon glyphicon-search blue" aria-hidden="true"></span> <span class="glyphicon glyphicon-search blue" aria-hidden="true"></span>
</button> </button>
</th> </th>
@ -80,8 +76,9 @@
</td> </td>
<td *ngIf="accessIdField.lookupField else accessIdInput" colspan="2" class="text-align text-width taskana-type-ahead"> <td *ngIf="accessIdField.lookupField else accessIdInput" colspan="2" class="text-align text-width taskana-type-ahead">
<div> <div>
<taskana-type-ahead formControlName="accessId" placeHolderMessage="* Access id is required" [validationValue]="toogleValidationAccessIdMap.get(index)" <taskana-type-ahead formControlName="accessId" placeHolderMessage="* Access id is required"
[displayError]="!isFieldValid('accessItem.value.accessId', index)" [disable]=true></taskana-type-ahead> [validationValue]="toogleValidationAccessIdMap.get(index)" [displayError]="!isFieldValid('accessItem.value.accessId', index)"
[disable]=true></taskana-type-ahead>
</div> </div>
</td> </td>
<ng-template #accessIdInput> <ng-template #accessIdInput>
@ -163,6 +160,41 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<button *ngIf="!isGroup" class="pull-left btn-group" type="button" class="btn btn-primary" data-toggle="modal"
data-target="#myModal">
Belonging groups
</button>
<div class="modal" id="myModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Belonging groups</h4>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<div class="modal-body">
<ul *ngIf="belongingGroups !== undefined && belongingGroups.length > 0 " class="list-group">
<li *ngFor="let group of belongingGroups" class="list-group-item">{{group.name}}</li>
</ul>
<p *ngIf="belongingGroups === undefined || belongingGroups.length === 0">The user is not associated to any groups</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div class="pull-right btn-group">
<button *ngIf="AccessItemsForm" type="button" class="btn btn-default" data-toggle="tooltip" title="Revoke access"
[disabled]=isGroup>
<span class="glyphicon glyphicon-remove red" aria-hidden="true"></span>
</button>
</div>
</form> </form>
</div> </div>
</div> </div>

View File

@ -31,3 +31,8 @@ td {
word-wrap:break-word; word-wrap:break-word;
} }
.min-width{ min-width: 135px;} .min-width{ min-width: 135px;}
.modal-title {
font-weight: bold;
display: inline;
}

View File

@ -32,8 +32,11 @@ export class AccessItemsManagementComponent implements OnInit, OnDestroy {
accessItemPermissionsSubscription: Subscription; accessItemPermissionsSubscription: Subscription;
accessItemInformationsubscription: Subscription; accessItemInformationsubscription: Subscription;
accessIdsWithGroups: Array<AccessIdDefinition>; accessIdsWithGroups: Array<AccessIdDefinition>;
belongingGroups: Array<AccessIdDefinition>;
sortingFields = new Map([['workbasket-key', 'Workbasket Key'], ['access-id', 'Access id']]); sortingFields = new Map([['workbasket-key', 'Workbasket Key'], ['access-id', 'Access id']]);
sortModel: SortingModel; sortModel: SortingModel;
isGroup: boolean;
groupsKey = 'ou=groups';
accessIdField = this.customFieldsService.getCustomField('Owner', 'workbaskets.access-items.accessId'); accessIdField = this.customFieldsService.getCustomField('Owner', 'workbaskets.access-items.accessId');
@ -89,11 +92,13 @@ export class AccessItemsManagementComponent implements OnInit, OnDestroy {
} }
if (!this.AccessItemsForm || this.accessIdPrevious !== selected.accessId) { if (!this.AccessItemsForm || this.accessIdPrevious !== selected.accessId) {
this.accessIdPrevious = selected.accessId this.accessIdPrevious = selected.accessId
this.isGroup = selected.accessId.includes(this.groupsKey);
this.unSubscribe(this.accessItemInformationsubscription) this.unSubscribe(this.accessItemInformationsubscription)
this.accessItemInformationsubscription = this.accessIdsService.getAccessItemsInformation(selected.accessId, true) this.accessItemInformationsubscription = this.accessIdsService.getAccessItemsInformation(selected.accessId, true)
.subscribe((accessIdsWithGroups: Array<AccessIdDefinition>) => { .subscribe((accessIdsWithGroups: Array<AccessIdDefinition>) => {
this.accessIdsWithGroups = accessIdsWithGroups; this.accessIdsWithGroups = accessIdsWithGroups;
this.belongingGroups = accessIdsWithGroups.filter(item => item.accessId.includes(this.groupsKey));
this.searchForAccessItemsWorkbaskets(); this.searchForAccessItemsWorkbaskets();
}, },
error => { error => {

View File

@ -21,7 +21,11 @@ export class AccessIdsService {
if (!token || token.length < 3) { if (!token || token.length < 3) {
return of([]); return of([]);
} }
return this.httpClient.get<Array<AccessIdDefinition>>(`${this.url}?searchFor=${token}&searchInGroups=${searchInGroups}`); if (searchInGroups) {
return this.httpClient.get<Array<AccessIdDefinition>>(`${this.url}/groups?access-id=${token}`);
} else {
return this.httpClient.get<Array<AccessIdDefinition>>(`${this.url}?search-for=${token}`);
}
}; };
getAccessItemsPermissions( getAccessItemsPermissions(

View File

@ -6,3 +6,7 @@
.sortby-dropdown { .sortby-dropdown {
min-width: 200px; min-width: 200px;
} }
ul {
list-style-type: none;
}

View File

@ -33,7 +33,7 @@
</span> </span>
<div class="input-group"> <div class="input-group">
<input #inputTypeAhead class=" form-control input-text" (blur)="typeaheadOnSelect({'item':dataSource.selected})" name="accessItem-{{index}}" <input #inputTypeAhead class=" form-control input-text" [ngClass]="{'invalid': dataSource.length == null}" (blur)="typeaheadOnSelect({'item':dataSource.selected})" name="accessItem-{{index}}"
required #accessItemName="ngModel" [(ngModel)]="value" [typeahead]="dataSource" typeaheadOptionField="name" [typeaheadItemTemplate]="customItemTemplate" required #accessItemName="ngModel" [(ngModel)]="value" [typeahead]="dataSource" typeaheadOptionField="name" [typeaheadItemTemplate]="customItemTemplate"
(typeaheadOnSelect)="typeaheadOnSelect($event, index)" [typeaheadScrollable]="true" [typeaheadOptionsInScrollableView]="typeaheadOptionsInScrollableView" (typeaheadOnSelect)="typeaheadOnSelect($event, index)" [typeaheadScrollable]="true" [typeaheadOptionsInScrollableView]="typeaheadOptionsInScrollableView"
[typeaheadMinLength]="typeaheadMinLength" [typeaheadWaitMs]="typeaheadWaitMs" (typeaheadLoading)="changeTypeaheadLoading($event)" [typeaheadMinLength]="typeaheadMinLength" [typeaheadWaitMs]="typeaheadWaitMs" (typeaheadLoading)="changeTypeaheadLoading($event)"

View File

@ -91,3 +91,7 @@
.disable { .disable {
cursor: not-allowed; cursor: not-allowed;
} }
.invalid {
color: $invalid;
}

View File

@ -9,3 +9,4 @@ $invalid: #d82626;
$aquamarine: #22a39f; $aquamarine: #22a39f;
$pallete-blue: #36bcee; $pallete-blue: #36bcee;
$pallete-green: #5fbca1; $pallete-green: #5fbca1;
$transparent-grey: rgba(192, 192, 192, 0.65);

View File

@ -353,3 +353,7 @@ li.list-group-item:hover {
outline: none; outline: none;
} }
} }
.modal-backdrop.show {
background-color: $transparent-grey;
}