Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2161,6 +2161,9 @@ public enum RODA_TYPE {
public static final String PERMISSION_METHOD_CREATE_GROUP = "org.roda.wui.api.v2.controller.MembersController.createGroup";
public static final String PERMISSION_METHOD_UPDATE_USER = "org.roda.wui.api.v2.controller.MembersController.updateUser";
public static final String PERMISSION_METHOD_DELETE_USER = "org.roda.wui.api.v2.controller.MembersController.deleteUser";
public static final String PERMISSION_METHOD_REVOKE_ACCESS_TOKEN = "org.roda.wui.api.v2.controller.MembersController.regenerateAccessKey";
public static final String PERMISSION_METHOD_DELETE_ACCESS_TOKEN = "org.roda.wui.api.v2.controller.MembersController.deleteAccessKey";
public static final String PERMISSION_METHOD_REGENERATE_ACCESS_TOKEN = "org.roda.wui.api.v2.controller.MembersController.regenerateAccessKey";

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

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.roda.core.common;

import org.roda.core.data.exceptions.GenericException;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
*
* @author Miguel Guimarães <mguimaraes@keep.pt>
*/
public class CryptographyUtils {

private CryptographyUtils() {
// do nothing
}

public static String hashTokenSHA256(String token) throws GenericException {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] encodedHash = digest.digest(token.getBytes(StandardCharsets.UTF_8));

// Convert the byte array into a hex string for easy storage
StringBuilder hexString = new StringBuilder(2 * encodedHash.length);
for (byte hash : encodedHash) {
String hex = Integer.toHexString(0xff & hash);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();

} catch (NoSuchAlgorithmException e) {
// Wrap and throw using your application's exception handling
throw new GenericException("Error initializing SHA-256 hashing algorithm", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.roda.core.RodaCoreFactory;
import org.roda.core.common.CryptographyUtils;
import org.roda.core.common.JwtUtils;
import org.roda.core.common.PremisV3Utils;
import org.roda.core.common.ProvidesInputStream;
Expand Down Expand Up @@ -214,6 +215,32 @@ public class DefaultModelService implements ModelService {
private JobRepository jobRepository;
private ReportRepository reportRepository;

public DefaultModelService(StorageService storage, EventsManager eventsManager, NodeType nodeType,
String instanceId) {
this.storage = storage;
this.eventsManager = eventsManager;
this.nodeType = nodeType;
this.instanceId = instanceId;
this.observers = new ArrayList<>();

if (RodaCoreFactory.checkIfWriteIsAllowed(nodeType)) {
ensureAllContainersExist();
ensureAllDirectoriesExist();
}
}

private static void clearSpecificIndexes(IndexService index, Class<IsRODAObject> objectClass,
IsModelObject rodaObject) throws AuthorizationDeniedException {
if (AIP.class.equals(objectClass)) {
List<String> ids = Arrays.asList(rodaObject.getId());
index.delete(IndexedRepresentation.class,
new Filter(new OneOfManyFilterParameter(RodaConstants.REPRESENTATION_AIP_ID, ids)));
index.delete(IndexedFile.class, new Filter(new OneOfManyFilterParameter(RodaConstants.FILE_AIP_ID, ids)));
index.delete(IndexedPreservationEvent.class,
new Filter(new OneOfManyFilterParameter(RodaConstants.PRESERVATION_EVENT_AIP_ID, ids)));
}
}

/**
* Lazily retrieves the JobRepository bean from Spring context.
*/
Expand Down Expand Up @@ -241,32 +268,6 @@ private boolean isJpaAvailable() {
return SpringContext.isContextInitialized();
}

public DefaultModelService(StorageService storage, EventsManager eventsManager, NodeType nodeType,
String instanceId) {
this.storage = storage;
this.eventsManager = eventsManager;
this.nodeType = nodeType;
this.instanceId = instanceId;
this.observers = new ArrayList<>();

if (RodaCoreFactory.checkIfWriteIsAllowed(nodeType)) {
ensureAllContainersExist();
ensureAllDirectoriesExist();
}
}

private static void clearSpecificIndexes(IndexService index, Class<IsRODAObject> objectClass,
IsModelObject rodaObject) throws AuthorizationDeniedException {
if (AIP.class.equals(objectClass)) {
List<String> ids = Arrays.asList(rodaObject.getId());
index.delete(IndexedRepresentation.class,
new Filter(new OneOfManyFilterParameter(RodaConstants.REPRESENTATION_AIP_ID, ids)));
index.delete(IndexedFile.class, new Filter(new OneOfManyFilterParameter(RodaConstants.FILE_AIP_ID, ids)));
index.delete(IndexedPreservationEvent.class,
new Filter(new OneOfManyFilterParameter(RodaConstants.PRESERVATION_EVENT_AIP_ID, ids)));
}
}

private void ensureAllContainersExist() {
try {
createContainerIfNotExists(RodaConstants.STORAGE_CONTAINER_AIP);
Expand Down Expand Up @@ -2805,7 +2806,40 @@ public Group updateGroup(final Group group, boolean notify, boolean isHandlingEv
} catch (IllegalOperationException e) {
throw new AuthorizationDeniedException("Illegal operation", e);
}
}

@Override
public Group updateGroupMembers(String id, Set<String> members, boolean notify, boolean isHandlingEvent)
throws AuthorizationDeniedException, NotFoundException, GenericException {
boolean writeIsAllowed = RodaCoreFactory.checkIfWriteIsAllowed(nodeType);

if (!writeIsAllowed && !isHandlingEvent) {
RodaCoreFactory.throwExceptionIfWriteIsNotAllowed();
}
Group group = UserUtility.getLdapUtility().getGroup(id);
group.getUsers().addAll(members);

for (String member: members) {
User user = UserUtility.getLdapUtility().getUser(member);
user.getGroups().add(id);
notifyUserUpdated(user).failOnError();
}

try {
Group updatedGroup = UserUtility.getLdapUtility().modifyGroupMembers(group);
if (notify && writeIsAllowed) {
notifyGroupUpdated(updatedGroup).failOnError();
}

if (!isHandlingEvent) {
// FIXME 20180813 hsilva: group is not the previous state of the group
eventsManager.notifyGroupUpdated(this, group, updatedGroup);
}

return updatedGroup;
} catch (IllegalOperationException e) {
throw new AuthorizationDeniedException("Illegal operation", e);
}
}

@Override
Expand Down Expand Up @@ -2942,8 +2976,8 @@ public void createOrUpdateJob(Job job)
}

/**
* Flushes a job and its reports from the database to the file storage.
* This method is called when a job reaches a final state.
* Flushes a job and its reports from the database to the file storage. This
* method is called when a job reaches a final state.
*/
private void flushJobToStorage(Job job)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
Expand Down Expand Up @@ -3008,8 +3042,7 @@ public CloseableIterable<OptionalWithCause<Report>> listJobReports(String jobId)
if (jobRepo != null && reportRepo != null && jobRepo.existsById(jobId)) {
// Return reports from database
List<Report> dbReports = reportRepo.findByJobId(jobId);
List<OptionalWithCause<Report>> wrappedReports = dbReports.stream()
.map(OptionalWithCause::of)
List<OptionalWithCause<Report>> wrappedReports = dbReports.stream().map(OptionalWithCause::of)
.collect(Collectors.toList());
return CloseableIterables.fromList(wrappedReports);
}
Expand Down Expand Up @@ -4081,13 +4114,13 @@ public <T extends IsRODAObject> CloseableIterable<OptionalWithCause<T>> list(Cla
} else if (DescriptiveMetadata.class.equals(objectClass)) {
ret = listDescriptiveMetadata();
} else if (Report.class.equals(objectClass)) {
// Include both DB reports (for running jobs) and storage reports (for completed jobs)
// Include both DB reports (for running jobs) and storage reports (for completed
// jobs)
CloseableIterable<OptionalWithCause<Report>> storageReports = ResourceParseUtils.convert(getStorage(),
listReportResources(), Report.class);
if (isJpaAvailable() && getReportRepository() != null) {
List<Report> dbReports = getReportRepository().findAll();
List<OptionalWithCause<Report>> wrappedDbReports = dbReports.stream()
.map(OptionalWithCause::of)
List<OptionalWithCause<Report>> wrappedDbReports = dbReports.stream().map(OptionalWithCause::of)
.collect(Collectors.toList());
CloseableIterable<OptionalWithCause<Report>> dbIterable = CloseableIterables.fromList(wrappedDbReports);
ret = CloseableIterables.concat(dbIterable, storageReports);
Expand All @@ -4102,8 +4135,7 @@ public <T extends IsRODAObject> CloseableIterable<OptionalWithCause<T>> list(Cla
resourcesIterable, Job.class);
if (isJpaAvailable() && getJobRepository() != null) {
List<Job> dbJobs = getJobRepository().findAll();
List<OptionalWithCause<Job>> wrappedDbJobs = dbJobs.stream()
.map(OptionalWithCause::of)
List<OptionalWithCause<Job>> wrappedDbJobs = dbJobs.stream().map(OptionalWithCause::of)
.collect(Collectors.toList());
CloseableIterable<OptionalWithCause<Job>> dbIterable = CloseableIterables.fromList(wrappedDbJobs);
ret = CloseableIterables.concat(dbIterable, storageJobs);
Expand Down Expand Up @@ -4144,13 +4176,13 @@ public <T extends IsRODAObject> CloseableIterable<OptionalWithCause<LiteRODAObje
ret = ResourceParseUtils.convertLite(getStorage(), ResourceListUtils.listDescriptiveMetadataResources(storage),
objectClass);
} else if (Report.class.equals(objectClass)) {
// Include both DB reports (for running jobs) and storage reports (for completed jobs)
// Include both DB reports (for running jobs) and storage reports (for completed
// jobs)
CloseableIterable<OptionalWithCause<LiteRODAObject>> storageReports = ResourceParseUtils.convertLite(getStorage(),
listReportResources(), objectClass);
if (isJpaAvailable() && getReportRepository() != null) {
List<Report> dbReports = getReportRepository().findAll();
List<OptionalWithCause<Report>> wrappedDbReports = dbReports.stream()
.map(OptionalWithCause::of)
List<OptionalWithCause<Report>> wrappedDbReports = dbReports.stream().map(OptionalWithCause::of)
.collect(Collectors.toList());
CloseableIterable<OptionalWithCause<LiteRODAObject>> dbIterable = LiteRODAObjectFactory
.transformIntoLite(CloseableIterables.fromList(wrappedDbReports));
Expand All @@ -4171,8 +4203,7 @@ public <T extends IsRODAObject> CloseableIterable<OptionalWithCause<LiteRODAObje
resourcesIterable, objectClass);
if (isJpaAvailable() && getJobRepository() != null) {
List<Job> dbJobs = getJobRepository().findAll();
List<OptionalWithCause<Job>> wrappedDbJobs = dbJobs.stream()
.map(OptionalWithCause::of)
List<OptionalWithCause<Job>> wrappedDbJobs = dbJobs.stream().map(OptionalWithCause::of)
.collect(Collectors.toList());
CloseableIterable<OptionalWithCause<LiteRODAObject>> dbIterable = LiteRODAObjectFactory
.transformIntoLite(CloseableIterables.fromList(wrappedDbJobs));
Expand Down Expand Up @@ -5266,9 +5297,10 @@ public AccessKey createAccessKey(AccessKey accessKey, String createdBy) throws G
accessKey.setExpirationDate(expirationDate);
}

String token = JwtUtils.generateToken(accessKey.getUserName(), accessKey.getExpirationDate());
String plainTextToken = JwtUtils.generateToken(accessKey.getUserName(), accessKey.getExpirationDate());

accessKey.setKey(token);
String hashedToken = CryptographyUtils.hashTokenSHA256(plainTextToken);
accessKey.setKey(hashedToken);

accessKey.setCreatedOn(new Date());
accessKey.setCreatedBy(createdBy);
Expand All @@ -5279,6 +5311,7 @@ public AccessKey createAccessKey(AccessKey accessKey, String createdBy) throws G
StoragePath accessKeyPath = ModelUtils.getAccessKeysStoragePath(accessKey.getId());
storage.createBinary(accessKeyPath, new StringContentPayload(accessKeyAsJson), false);

accessKey.setKey(plainTextToken);
return accessKey;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import org.roda.core.common.ReturnWithExceptionsWrapper;
import org.roda.core.common.iterables.CloseableIterable;
Expand Down Expand Up @@ -2038,6 +2039,12 @@ public Group updateGroup(Group group, boolean notify, boolean isHandlingEvent)
return mainModelService.updateGroup(group, notify, isHandlingEvent);
}

@Override
public Group updateGroupMembers(String id, Set<String> members, boolean notify, boolean isHandlingEvent)
throws GenericException, NotFoundException, AuthorizationDeniedException {
return mainModelService.updateGroupMembers(id, members, notify, isHandlingEvent);
}

@Override
public void deleteGroup(String id, boolean notify) throws GenericException, AuthorizationDeniedException {
mainModelService.deleteGroup(id, notify);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import org.roda.core.common.iterables.CloseableIterable;
import org.roda.core.common.notifications.NotificationProcessor;
Expand Down Expand Up @@ -547,6 +548,8 @@ Group updateGroup(Group group, boolean notify)
Group updateGroup(Group group, boolean notify, boolean isHandlingEvent)
throws GenericException, NotFoundException, AuthorizationDeniedException;

Group updateGroupMembers(String id, Set<String> members, boolean notify,boolean isHandlingEvent) throws AuthorizationDeniedException, NotFoundException, GenericException;

void deleteGroup(String id, boolean notify) throws GenericException, AuthorizationDeniedException;

void deleteGroup(String id, boolean notify, boolean isHandlingEvent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,10 @@ public Group modifyGroup(final Group modifiedGroup)
return modifyGroup(modifiedGroup, false);
}

public Group modifyGroupMembers(final Group modifiedGroup) throws NotFoundException, IllegalOperationException, GenericException {
return modifyGroup(modifiedGroup, true);
}

/**
* Removes a group.
*
Expand Down Expand Up @@ -1809,6 +1813,9 @@ private Name removeBaseDN(Name dn) {
return LdapUtils.removeFirst(dn, LdapUtils.newLdapName(ldapRootDN));
}

public boolean isProtectedUser(String username) {
return this.ldapProtectedUsers.contains(username);
}

public String transformExtra(Set<MetadataValue> values) {
Handlebars handlebars = new Handlebars();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ core.roles.org.roda.wui.api.v2.controller.MembersController.updateAccessKey = ac
core.roles.org.roda.wui.api.v2.controller.MembersController.getAccessKeysByUser = access_key.read
core.roles.org.roda.wui.api.v2.controller.MembersController.createAccessKey = access_key.manage
core.roles.org.roda.wui.api.v2.controller.MembersController.changeActive = member.manage
core.roles.org.roda.wui.api.v2.controller.MembersController.addGroupsToUser = member.manage
core.roles.org.roda.wui.api.v2.controller.MembersController.removeGroupsFromUser = member.manage
core.roles.org.roda.wui.api.v2.controller.MembersController.getUserGroups = member.read
core.roles.org.roda.wui.api.v2.controller.MembersController.getGroupMembers = member.read
core.roles.org.roda.wui.api.v2.controller.MembersController.addMembersToGroup = member.manage
core.roles.org.roda.wui.api.v2.controller.MembersController.removeMembersFromGroup = member.manage
core.roles.org.roda.wui.api.v2.services.MembersService.retrieveOwnUserExtra = member.read
core.roles.org.roda.wui.api.v2.services.MembersService.retrieveUserExtra = member.read

Expand Down
Loading
Loading