Skip to content

Commit

Permalink
SEBSERV-452 applied ExamPrivilege and FeaturePrivilege to UserInfo fo…
Browse files Browse the repository at this point in the history
…r future privilege improvements
  • Loading branch information
anhefti committed Dec 5, 2023
1 parent 4cc1eca commit 5bea674
Show file tree
Hide file tree
Showing 14 changed files with 357 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

/** Defines a Privilege by combining a PrivilegeType for base (overall) rights,
* institutional rights and ownership rights.
*
* <p>
* A base-, institutional- and ownership- grant is checked in this exact order and the
* first match fund makes a grant or a denied if none of the three privilege levels has a match */
public final class Privilege {
Expand Down Expand Up @@ -87,11 +87,11 @@ public boolean hasOwnershipPrivilege(final PrivilegeType privilegeType) {
}

/** Checks if this Privilege has a grant for a given context.
*
* <p>
* The privilege grant check function always checks first the base privilege with no institutional or owner grant.
* If user has a grant on base privileges this returns true without checking further institutional or owner grant
* If user has no base privilege grant the function checks further grants, first the institutional grant, where
* the institution id and the users institution id must match and further more the owner grant, where ownerId
* the institution id and the users institution id must match and furthermore the owner grant, where ownerId
* and the users id must match.
*
* @param userId The user identifier of the user to check the grant on
Expand Down Expand Up @@ -184,7 +184,7 @@ public String toString() {
}

/** Checks if the current user has role based edit access to a specified user account.
*
* <p>
* If user account has UserRole.SEB_SERVER_ADMIN this always gives true
* If user account has UserRole.INSTITUTIONAL_ADMIN this is true if the given user account has
* not the UserRole.SEB_SERVER_ADMIN (institutional administrators should not be able to edit SEB Server
Expand All @@ -197,17 +197,15 @@ public String toString() {
public static boolean hasRoleBasedUserAccountEditGrant(final UserAccount userAccount, final UserInfo currentUser) {
final EnumSet<UserRole> userRolesOfUserAccount = userAccount.getUserRoles();
final EnumSet<UserRole> userRolesOfCurrentUser = currentUser.getUserRoles();

if (userRolesOfCurrentUser.contains(UserRole.SEB_SERVER_ADMIN)) {
return true;
}

if (userRolesOfCurrentUser.contains(UserRole.INSTITUTIONAL_ADMIN)) {
return !userRolesOfUserAccount.contains(UserRole.SEB_SERVER_ADMIN);
}
if (currentUser.equals(userAccount)) {
return true;
}

return false;
return currentUser.equals(userAccount);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,38 @@

package ch.ethz.seb.sebserver.gbl.api.authorization;

import java.util.Arrays;

/** Defines SEB-Server internal privilege types **/
public enum PrivilegeType {
/** No privilege type at all (placeholder) */
NONE,
NONE(0, 'n'),
/** Only assigned entity privileges for the specific entity type. This is used as a marker to indicate that
* the user has no overall entity type privileges but might have assigned entity privileges. */
ASSIGNED(1, 'a'),
/** The read privilege type for read access */
READ,
READ( 2, 'r'),
/** The modify privilege type includes read-only type privilege plus privilege for editing right but without create
* and delete
* rights */
MODIFY,
* and delete rights */
MODIFY(3, 'm'),
/** The write privilege type including modify privilege type plus creation and deletion rights */
WRITE;
WRITE(4, 'w');

public final byte key;
public final char code;

PrivilegeType(final int key, final char code) {
this.key = (byte) key;
this.code = code;
}

public static PrivilegeType byKey(final byte key) {
return Arrays.stream(PrivilegeType.values()).filter(t -> t.key == key).findFirst().orElse(NONE);
}

public static PrivilegeType byCode(final char code) {
return Arrays.stream(PrivilegeType.values()).filter(t -> t.code == code).findFirst().orElse(NONE);
}

/** Use this to check implicit privilege.
* <p>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright (c) 2023 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

package ch.ethz.seb.sebserver.gbl.model.user;



import java.util.Objects;

import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
import ch.ethz.seb.sebserver.gbl.model.Domain.*;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class EntityPrivilege {

@JsonProperty(ENTITY_PRIVILEGE.ATTR_ID)
public final Long id;

@JsonProperty(ENTITY_PRIVILEGE.ATTR_ENTITY_TYPE)
public final EntityType entityType;

@JsonProperty(ENTITY_PRIVILEGE.ATTR_ENTITY_ID)
public final Long entityId;

@JsonProperty(ENTITY_PRIVILEGE.ATTR_USER_UUID)
public final String userUUID;

@JsonProperty(ENTITY_PRIVILEGE.ATTR_PRIVILEGE_TYPE)
public final PrivilegeType privilegeType;

@JsonCreator
public EntityPrivilege(
@JsonProperty(ENTITY_PRIVILEGE.ATTR_ID) final Long id,
@JsonProperty(ENTITY_PRIVILEGE.ATTR_ENTITY_TYPE) final EntityType entityType,
@JsonProperty(ENTITY_PRIVILEGE.ATTR_ENTITY_ID) final Long entityId,
@JsonProperty(ENTITY_PRIVILEGE.ATTR_USER_UUID) final String userUUID,
@JsonProperty(ENTITY_PRIVILEGE.ATTR_PRIVILEGE_TYPE) final PrivilegeType privilegeType) {

this.id = id;
this.entityType = entityType;
this.entityId = entityId;
this.userUUID = userUUID;
this.privilegeType = privilegeType;
}

public Long getId() {
return this.id;
}

public EntityType getEntityType() {
return this.entityType;
}

public Long getEntityId() {
return this.entityId;
}

public String getUserUUID() {
return this.userUUID;
}

public PrivilegeType getPrivilegeType() {
return this.privilegeType;
}

@Override
public int hashCode() {
return Objects.hash(this.id);
}

@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final EntityPrivilege other = (EntityPrivilege) obj;
return Objects.equals(this.id, other.id);
}

@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("EntityPrivilege [id=");
builder.append(this.id);
builder.append(", entityType=");
builder.append(this.entityType);
builder.append(", entityId=");
builder.append(this.entityId);
builder.append(", userUUID=");
builder.append(this.userUUID);
builder.append(", privilegeType=");
builder.append(this.privilegeType);
builder.append("]");
return builder.toString();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package ch.ethz.seb.sebserver.gbl.model.user;

import java.util.Objects;

import ch.ethz.seb.sebserver.gbl.model.Domain.*;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class FeaturePrivilege {

@JsonProperty(FEATURE_PRIVILEGE.ATTR_ID)
public final Long id;
@JsonProperty(FEATURE_PRIVILEGE.ATTR_FEATURE_ID)
public final Long featureId;

@JsonProperty(FEATURE_PRIVILEGE.ATTR_USER_UUID)
public final String userUUID;

public FeaturePrivilege(
@JsonProperty(FEATURE_PRIVILEGE.ATTR_ID) final Long id,
@JsonProperty(FEATURE_PRIVILEGE.ATTR_FEATURE_ID) final Long featureId,
@JsonProperty(FEATURE_PRIVILEGE.ATTR_USER_UUID) final String userUUID) {

this.id = id;
this.featureId = featureId;
this.userUUID = userUUID;
}
public Long getId() {
return id;
}

public Long getFeatureId() {
return featureId;
}

public String getUserUUID() {
return userUUID;
}

@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final FeaturePrivilege that = (FeaturePrivilege) o;
return Objects.equals(id, that.id);
}

@Override
public int hashCode() {
return Objects.hash(id);
}

@Override
public String toString() {
return "FeaturePrivilege{" +
"id=" + id +
", featureId=" + featureId +
", userUUID='" + userUUID + '\'' +
'}';
}
}
50 changes: 35 additions & 15 deletions src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,23 @@

package ch.ethz.seb.sebserver.gbl.model.user;

import java.io.Serial;
import java.io.Serializable;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import com.fasterxml.jackson.annotation.*;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.springframework.util.CollectionUtils;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Domain.USER;
import ch.ethz.seb.sebserver.gbl.model.Domain.USER_ROLE;
Expand All @@ -40,16 +33,20 @@
import ch.ethz.seb.sebserver.gbl.util.Utils;

/** The user info domain model contains primary user information
*
* <p>
* This domain model is annotated and fully serializable and deserializable
* to and from JSON within the Jackson library.
*
* <p>
* This domain model is immutable and thread-save */
@JsonIgnoreProperties(ignoreUnknown = true)
public final class UserInfo implements UserAccount, Serializable {

@Serial
private static final long serialVersionUID = 2526446136264377808L;

public static final String ATTR_ENTITY_PRIVILEGES = "entityPrivileges";
public static final String ATTR_FEATURE_PRIVILEGES = "featurePrivileges";

public static final String FILTER_ATTR_SURNAME = "surname";
public static final String FILTER_ATTR_USER_NAME = "username";
public static final String FILTER_ATTR_EMAIL = "email";
Expand Down Expand Up @@ -112,6 +109,13 @@ public final class UserInfo implements UserAccount, Serializable {
@JsonProperty(USER_ROLE.REFERENCE_NAME)
public final Set<String> roles;

@JsonProperty(ATTR_ENTITY_PRIVILEGES)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public final Collection<EntityPrivilege> entityPrivileges;
@JsonProperty(ATTR_FEATURE_PRIVILEGES)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public final Collection<FeaturePrivilege> featurePrivileges;

@JsonCreator
public UserInfo(
@JsonProperty(USER.ATTR_UUID) final String uuid,
Expand All @@ -124,7 +128,9 @@ public UserInfo(
@JsonProperty(USER.ATTR_ACTIVE) final Boolean active,
@JsonProperty(USER.ATTR_LANGUAGE) final Locale language,
@JsonProperty(USER.ATTR_TIMEZONE) final DateTimeZone timeZone,
@JsonProperty(USER_ROLE.REFERENCE_NAME) final Set<String> roles) {
@JsonProperty(USER_ROLE.REFERENCE_NAME) final Set<String> roles,
@JsonProperty(ATTR_ENTITY_PRIVILEGES) final Collection<EntityPrivilege> entityPrivileges,
@JsonProperty(ATTR_FEATURE_PRIVILEGES) final Collection<FeaturePrivilege> featurePrivileges) {

this.uuid = uuid;
this.institutionId = institutionId;
Expand All @@ -137,6 +143,8 @@ public UserInfo(
this.language = language;
this.timeZone = timeZone;
this.roles = Utils.immutableSetOf(roles);
this.entityPrivileges = Utils.immutableCollectionOf(entityPrivileges);
this.featurePrivileges = Utils.immutableCollectionOf(featurePrivileges);
}

@Override
Expand Down Expand Up @@ -213,6 +221,14 @@ public Set<String> getRoles() {
return this.roles;
}

public Collection<EntityPrivilege> getEntityPrivileges() {
return entityPrivileges;
}

public Collection<FeaturePrivilege> getFeaturePrivileges() {
return featurePrivileges;
}

@Override
@JsonIgnore
public EnumSet<UserRole> getUserRoles() {
Expand Down Expand Up @@ -323,7 +339,9 @@ public static UserInfo of(final UserInfo userInfo) {
userInfo.getActive(),
userInfo.getLanguage(),
userInfo.getTimeZone(),
userInfo.roles);
userInfo.roles,
userInfo.entityPrivileges,
userInfo.featurePrivileges);
}

/** Use this to create a copy of a given UserInfo by overriding available arguments.
Expand Down Expand Up @@ -358,7 +376,9 @@ public static UserInfo of(
userInfo.getActive(),
(language != null) ? language : userInfo.getLanguage(),
(timeZone != null) ? timeZone : userInfo.getTimeZone(),
(roles != null) ? new HashSet<>(Arrays.asList(roles)) : userInfo.roles);
(roles != null) ? new HashSet<>(Arrays.asList(roles)) : userInfo.roles,
userInfo.entityPrivileges,
userInfo.featurePrivileges);
}

public static UserInfo withEMail(final UserInfo userInfo, final String email) {
Expand Down
Loading

0 comments on commit 5bea674

Please sign in to comment.