Skip to content

Commit a5fdb88

Browse files
committed
wip
1 parent 1b4a43c commit a5fdb88

34 files changed

Lines changed: 1209 additions & 820 deletions

roda-common/roda-common-data/src/main/java/org/roda/core/data/common/RodaConstants.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2158,6 +2158,9 @@ public enum RODA_TYPE {
21582158
public static final String PERMISSION_METHOD_CREATE_GROUP = "org.roda.wui.api.v2.controller.MembersController.createGroup";
21592159
public static final String PERMISSION_METHOD_UPDATE_USER = "org.roda.wui.api.v2.controller.MembersController.updateUser";
21602160
public static final String PERMISSION_METHOD_DELETE_USER = "org.roda.wui.api.v2.controller.MembersController.deleteUser";
2161+
public static final String PERMISSION_METHOD_REVOKE_ACCESS_TOKEN = "org.roda.wui.api.v2.controller.MembersController.regenerateAccessKey";
2162+
public static final String PERMISSION_METHOD_DELETE_ACCESS_TOKEN = "org.roda.wui.api.v2.controller.MembersController.deleteAccessKey";
2163+
public static final String PERMISSION_METHOD_REGENERATE_ACCESS_TOKEN = "org.roda.wui.api.v2.controller.MembersController.regenerateAccessKey";
21612164

21622165
public static final String PERMISSION_METHOD_CREATE_ACCESS_KEY = "org.roda.wui.api.v2.controller.MembersController.createAccessKey";
21632166

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.roda.core.common;
2+
3+
import org.roda.core.data.exceptions.GenericException;
4+
5+
import java.nio.charset.StandardCharsets;
6+
import java.security.MessageDigest;
7+
import java.security.NoSuchAlgorithmException;
8+
9+
/**
10+
*
11+
* @author Miguel Guimarães <mguimaraes@keep.pt>
12+
*/
13+
public class CryptographyUtils {
14+
15+
private CryptographyUtils() {
16+
// do nothing
17+
}
18+
19+
public static String hashTokenSHA256(String token) throws GenericException {
20+
try {
21+
MessageDigest digest = MessageDigest.getInstance("SHA-256");
22+
byte[] encodedHash = digest.digest(token.getBytes(StandardCharsets.UTF_8));
23+
24+
// Convert the byte array into a hex string for easy storage
25+
StringBuilder hexString = new StringBuilder(2 * encodedHash.length);
26+
for (byte hash : encodedHash) {
27+
String hex = Integer.toHexString(0xff & hash);
28+
if (hex.length() == 1) {
29+
hexString.append('0');
30+
}
31+
hexString.append(hex);
32+
}
33+
return hexString.toString();
34+
35+
} catch (NoSuchAlgorithmException e) {
36+
// Wrap and throw using your application's exception handling
37+
throw new GenericException("Error initializing SHA-256 hashing algorithm", e);
38+
}
39+
}
40+
}

roda-core/roda-core/src/main/java/org/roda/core/model/DefaultModelService.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.apache.commons.io.IOUtils;
5353
import org.apache.commons.lang3.StringUtils;
5454
import org.roda.core.RodaCoreFactory;
55+
import org.roda.core.common.CryptographyUtils;
5556
import org.roda.core.common.JwtUtils;
5657
import org.roda.core.common.PremisV3Utils;
5758
import org.roda.core.common.ProvidesInputStream;
@@ -5266,9 +5267,10 @@ public AccessKey createAccessKey(AccessKey accessKey, String createdBy) throws G
52665267
accessKey.setExpirationDate(expirationDate);
52675268
}
52685269

5269-
String token = JwtUtils.generateToken(accessKey.getUserName(), accessKey.getExpirationDate());
5270+
String plainTextToken = JwtUtils.generateToken(accessKey.getUserName(), accessKey.getExpirationDate());
52705271

5271-
accessKey.setKey(token);
5272+
String hashedToken = CryptographyUtils.hashTokenSHA256(plainTextToken);
5273+
accessKey.setKey(hashedToken);
52725274

52735275
accessKey.setCreatedOn(new Date());
52745276
accessKey.setCreatedBy(createdBy);
@@ -5279,6 +5281,7 @@ public AccessKey createAccessKey(AccessKey accessKey, String createdBy) throws G
52795281
StoragePath accessKeyPath = ModelUtils.getAccessKeysStoragePath(accessKey.getId());
52805282
storage.createBinary(accessKeyPath, new StringContentPayload(accessKeyAsJson), false);
52815283

5284+
accessKey.setKey(plainTextToken);
52825285
return accessKey;
52835286
}
52845287

roda-ui/roda-wui/src/main/java/config/i18n/client/ClientMessages.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2501,6 +2501,8 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String
25012501

25022502
String createAccessKeyTitle();
25032503

2504+
String regenerateAccessKeyTitle();
2505+
25042506
String showAccessKeyTitle();
25052507

25062508
String editAccessKeyTitle();
@@ -2539,6 +2541,8 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String
25392541

25402542
String accessKeySuccessfullyRegenerated();
25412543

2544+
String accessKeySuccessfullyDeleted();
2545+
25422546
String accessKeySuccessfullyRevoked();
25432547

25442548
String accessKeyDeleteConfirmationMessage();
@@ -2547,6 +2551,8 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String
25472551

25482552
String accessKeyRegenerateConfirmationMessage();
25492553

2554+
String accessKeyExpirationDateInThePast();
2555+
25502556
/** Market **/
25512557
String marketPluginsActionsTabLabel();
25522558

@@ -2641,4 +2647,15 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String
26412647
String reasonCantActOnUser();
26422648

26432649
String reasonCantActOnGroup();
2650+
2651+
// RODA Members - Permissions
2652+
String catalogueAndSearchGroupLabel();
2653+
2654+
String ingestPreservationActionsInternalActionsGroupLabel();
2655+
2656+
String administrationGroupLabel();
2657+
2658+
String planningGroupLabel();
2659+
2660+
String disposalGroupLabel();
26442661
}

roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/MembersController.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ private static Set<String> mapCasGroupstoRODAGroups(Set<String> casGroups) {
272272
}
273273

274274
@Override
275-
public User getUser(String name) {
275+
public RODAMember getUser(String name) {
276276
RequestContext requestContext = RequestUtils.parseHTTPRequest(request);
277277
ControllerAssistant controllerAssistant = new ControllerAssistant() {};
278278
LogEntryState state = LogEntryState.SUCCESS;
@@ -281,7 +281,11 @@ public User getUser(String name) {
281281
// check user permissions
282282
controllerAssistant.checkRoles(requestContext.getUser());
283283

284-
return membersService.retrieveUser(name);
284+
if (name.startsWith("user-")) {
285+
return membersService.retrieveUser(name.substring("user-".length()));
286+
} else {
287+
return membersService.retrieveGroup(name.substring("group-".length()));
288+
}
285289

286290
} catch (RODAException e) {
287291
state = LogEntryState.FAILURE;

roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/RODAMemberTabs.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@
22

33
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
44
import com.google.gwt.user.client.ui.Widget;
5+
import org.roda.core.data.common.RodaConstants;
56
import org.roda.core.data.v2.user.RODAMember;
7+
import org.roda.core.data.v2.user.User;
8+
import org.roda.wui.client.common.utils.PermissionClientUtils;
69
import org.roda.wui.client.management.members.tabs.AccessKeysTab;
10+
import org.roda.wui.client.management.members.tabs.RODAMemberGroupsTab;
11+
import org.roda.wui.client.management.members.tabs.RODAMemberPermissionsTab;
712

813
/**
914
* @author Miguel Guimarães <mguimaraes@keep.pt>
@@ -19,11 +24,28 @@ public Widget buildTabWidget() {
1924
}
2025
});
2126

22-
createAndAddTab(SafeHtmlUtils.fromSafeConstant("Access Tokens"), new TabContentBuilder() {
27+
createAndAddTab(SafeHtmlUtils.fromSafeConstant(member.isUser() ? messages.groups() : messages.users()), new TabContentBuilder() {
2328
@Override
2429
public Widget buildTabWidget() {
25-
return new AccessKeysTab(member);
30+
return new RODAMemberGroupsTab(member);
2631
}
2732
});
33+
34+
createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.permissionsTab()), new TabContentBuilder() {
35+
@Override
36+
public Widget buildTabWidget() {
37+
return new RODAMemberPermissionsTab(member);
38+
}
39+
});
40+
41+
if (member.isUser() && PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_REVOKE_ACCESS_TOKEN,
42+
RodaConstants.PERMISSION_METHOD_REGENERATE_ACCESS_TOKEN, RodaConstants.PERMISSION_METHOD_DELETE_ACCESS_TOKEN)) {
43+
createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.showAccessKeyTitle()), new TabContentBuilder() {
44+
@Override
45+
public Widget buildTabWidget() {
46+
return new AccessKeysTab(member);
47+
}
48+
});
49+
}
2850
}
2951
}

roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/UserActionsToolbar.java renamed to roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/RODAMemberActionsToolbar.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import java.util.List;
1111

1212
import org.roda.core.data.v2.user.RODAMember;
13-
import org.roda.core.data.v2.user.User;
1413
import org.roda.wui.client.common.actions.RODAMemberAction;
1514
import org.roda.wui.client.common.actions.RODAMemberToolbarActions;
1615
import org.roda.wui.client.common.actions.model.ActionableObject;
@@ -20,9 +19,13 @@
2019
*
2120
* @author Miguel Guimarães <mguimaraes@keep.pt>
2221
*/
23-
public class UserActionsToolbar extends BrowseObjectActionsToolbar<User> {
22+
public class RODAMemberActionsToolbar extends BrowseObjectActionsToolbar<RODAMember> {
2423
public void buildIcon() {
25-
setIcon("fa fa-user");
24+
if (object.isUser()) {
25+
setIcon("fa fa-user");
26+
} else {
27+
setIcon("fa fa-group");
28+
}
2629
}
2730

2831
public void buildTags() {

roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberToolbarActions.java

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.roda.core.data.v2.accessKey.CreateAccessKeyRequest;
88
import org.roda.core.data.v2.user.RODAMember;
99
import org.roda.core.data.v2.user.requests.ChangeUserStatusRequest;
10+
import org.roda.wui.client.common.NoAsyncCallback;
1011
import org.roda.wui.client.common.actions.callbacks.ActionNoAsyncCallback;
1112
import org.roda.wui.client.common.actions.model.ActionableBundle;
1213
import org.roda.wui.client.common.actions.model.ActionableGroup;
@@ -16,12 +17,12 @@
1617
import org.roda.wui.client.management.members.EditGroup;
1718
import org.roda.wui.client.management.members.EditUser;
1819
import org.roda.wui.client.management.members.MemberManagement;
20+
import org.roda.wui.client.management.members.ShowUser;
1921
import org.roda.wui.client.services.Services;
2022
import org.roda.wui.common.client.tools.HistoryUtils;
2123
import org.roda.wui.common.client.widgets.Toast;
2224

2325
import java.util.Arrays;
24-
import java.util.Date;
2526
import java.util.HashSet;
2627
import java.util.Set;
2728

@@ -206,12 +207,28 @@ public void onFailure(Throwable caught) {
206207

207208
private void createNewAccessKey(AsyncCallback<ActionImpact> callback, RODAMember user) {
208209
callback.onSuccess(ActionImpact.NONE);
209-
AccessKeyDialogs.showRegenerateAccessKeyDialog("test", null, true, new ActionNoAsyncCallback<CreateAccessKeyRequest>(callback) {
210-
@Override
211-
public void onSuccess(CreateAccessKeyRequest result) {
212-
super.onSuccess(result);
213-
}
214-
});
210+
AccessKeyDialogs.createAccessKeyDialog(messages.createAccessKeyTitle(), null, true,
211+
new ActionNoAsyncCallback<CreateAccessKeyRequest>(callback) {
212+
@Override
213+
public void onSuccess(CreateAccessKeyRequest keyRequest) {
214+
Services services = new Services("Create access key", "create");
215+
CreateAccessKeyRequest createAccessKeyRequest = new CreateAccessKeyRequest(keyRequest.getName(),
216+
keyRequest.getExpirationDate());
217+
services.membersResource(s -> s.createAccessKey(user.getId(), createAccessKeyRequest))
218+
.whenComplete((response, error) -> {
219+
if (response != null) {
220+
AccessKeyDialogs.showAccessKeyDialog(messages.accessKeyLabel(), response,
221+
new NoAsyncCallback<Boolean>() {
222+
@Override
223+
public void onSuccess(Boolean result) {
224+
callback.onSuccess(ActionImpact.UPDATED);
225+
HistoryUtils.newHistory(ShowUser.RESOLVER, user.getId());
226+
}
227+
});
228+
}
229+
});
230+
}
231+
});
215232
}
216233

217234
@Override

roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/AccessKeyDialogs.java

Lines changed: 12 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
package org.roda.wui.client.common.dialogs;
99

1010
import com.google.gwt.i18n.client.DateTimeFormat;
11-
import com.google.gwt.user.client.ui.SimplePanel;
1211
import com.google.gwt.user.client.ui.TextBox;
1312
import com.google.gwt.user.datepicker.client.DateBox;
1413
import org.roda.core.data.v2.accessKey.AccessKey;
@@ -103,66 +102,27 @@ public void onClick(ClickEvent clickEvent) {
103102
dialogBox.show();
104103
}
105104

106-
public static void showCreateAccessTokenModal(final AsyncCallback<String> callback) {
107-
final DialogBox dialogBox = new DialogBox(false, true);
108-
dialogBox.setText("Create access token");
109-
110-
final FlowPanel layout = new FlowPanel();
111-
112-
final Label messageLabel = new Label("test");
113-
layout.add(messageLabel);
114-
messageLabel.addStyleName("wui-dialog-message");
115-
116-
final Button cancelButton = new Button(messages.cancelButton());
117-
final Button confirmButton = new Button(messages.saveButton());
118-
119-
layout.add(cancelButton);
120-
layout.add(confirmButton);
121-
122-
dialogBox.setWidget(layout);
123-
124-
dialogBox.setGlassEnabled(true);
125-
dialogBox.setAnimationEnabled(false);
126-
127-
cancelButton.addClickHandler(new ClickHandler() {
128-
129-
@Override
130-
public void onClick(ClickEvent event) {
131-
dialogBox.hide();
132-
callback.onFailure(null);
133-
}
134-
});
135-
136-
dialogBox.addStyleName("wui-dialog-prompt");
137-
layout.addStyleName("wui-dialog-layout");
138-
cancelButton.addStyleName("btn btn-link");
139-
confirmButton.addStyleName("pull-right btn btn-play");
140-
141-
dialogBox.center();
142-
dialogBox.show();
143-
}
144-
145-
public static void showRegenerateAccessKeyDialog(String title, String tokenName, boolean create,
146-
final AsyncCallback<CreateAccessKeyRequest> callback) {
105+
public static void createAccessKeyDialog(String title, String tokenName, boolean create,
106+
final AsyncCallback<CreateAccessKeyRequest> callback) {
147107
final DialogBox dialogBox = new DialogBox(false, true);
148108
final Button cancelButton = new Button(messages.cancelButton());
149109
final Button confirmButton = new Button(messages.confirmButton());
150110
final FlowPanel layout = new FlowPanel();
151111
final FlowPanel header = new FlowPanel();
152112
final FlowPanel footer = new FlowPanel();
153113

154-
final Label tokenNameLabel = new Label("Name");
114+
final Label tokenNameLabel = new Label(messages.accessKeyNameLabel());
155115
final TextBox tokenNameTextBox = new TextBox();
156-
final Label tokenNameTextBoxErrorLabel = new Label("Mandatory field");
116+
final Label tokenNameTextBoxErrorLabel = new Label(messages.mandatoryField());
157117

158118
if (!create) {
159119
tokenNameTextBox.setText(tokenName);
160120
tokenNameTextBox.setEnabled(false);
161121
}
162122

163-
final Label expirationDateLabel = new Label("Expiration date");
123+
final Label expirationDateLabel = new Label(messages.accessKeyExpirationDateLabel());
164124
final DateBox expirationDateBox = new DateBox();
165-
final Label expirationDateErrorLabel = new Label("Invalid Date");
125+
final Label expirationDateErrorLabel = new Label();
166126

167127
tokenNameTextBoxErrorLabel.addStyleName("form-label-error");
168128
tokenNameTextBoxErrorLabel.setVisible(false);
@@ -204,8 +164,13 @@ public void onClick(ClickEvent clickEvent) {
204164
}
205165

206166
Date selectedDate = expirationDateBox.getValue();
207-
if (selectedDate == null || selectedDate.before(new Date())) {
167+
if (selectedDate == null) {
168+
expirationDateErrorLabel.setVisible(true);
169+
expirationDateErrorLabel.setText(messages.mandatoryField());
170+
errors = true;
171+
} else if (selectedDate.before(new Date())) {
208172
expirationDateErrorLabel.setVisible(true);
173+
expirationDateErrorLabel.setText(messages.accessKeyExpirationDateInThePast());
209174
errors = true;
210175
} else {
211176
expirationDateErrorLabel.setVisible(false);

0 commit comments

Comments
 (0)