io.gravitee.rest.api.service.impl.RoleServiceImpl Maven / Gradle / Ivy
/**
* Copyright (C) 2015 The Gravitee team (http://gravitee.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gravitee.rest.api.service.impl;
import static io.gravitee.repository.management.model.Audit.AuditProperties.ROLE;
import static io.gravitee.repository.management.model.Role.AuditEvent.*;
import static io.gravitee.rest.api.model.permissions.ApiPermission.REVIEWS;
import static io.gravitee.rest.api.model.permissions.RolePermissionAction.*;
import static io.gravitee.rest.api.service.common.DefaultRoleEntityDefinition.*;
import static java.util.Arrays.stream;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
import io.gravitee.repository.exceptions.TechnicalException;
import io.gravitee.repository.management.api.RoleRepository;
import io.gravitee.repository.management.model.Role;
import io.gravitee.repository.management.model.RoleReferenceType;
import io.gravitee.rest.api.model.MembershipReferenceType;
import io.gravitee.rest.api.model.NewRoleEntity;
import io.gravitee.rest.api.model.RoleEntity;
import io.gravitee.rest.api.model.UpdateRoleEntity;
import io.gravitee.rest.api.model.permissions.*;
import io.gravitee.rest.api.service.AuditService;
import io.gravitee.rest.api.service.MembershipService;
import io.gravitee.rest.api.service.RoleService;
import io.gravitee.rest.api.service.common.GraviteeContext;
import io.gravitee.rest.api.service.common.RandomString;
import io.gravitee.rest.api.service.exceptions.*;
import java.util.*;
import java.util.stream.Stream;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author Nicolas GERAUD (nicolas.geraud at graviteesource.com)
* @author GraviteeSource Team
*/
@Component
public class RoleServiceImpl extends AbstractService implements RoleService {
private final Logger LOGGER = LoggerFactory.getLogger(RoleServiceImpl.class);
@Autowired
private RoleRepository roleRepository;
@Autowired
private MembershipService membershipService;
@Autowired
private AuditService auditService;
private Map apiPrimaryOwnersByOrganization = new HashMap<>();
private Map applicationPrimaryOwnersByOrganization = new HashMap<>();
@Override
public RoleEntity findById(final String roleId) {
return GraviteeContext
.getCurrentRoles()
.computeIfAbsent(
roleId,
k -> {
try {
LOGGER.debug("Find Role by id");
Optional role = roleRepository.findById(k);
if (!role.isPresent()) {
throw new RoleNotFoundException(k);
}
return convert(role.get());
} catch (TechnicalException ex) {
LOGGER.error("An error occurs while trying to find a role : {}", k, ex);
throw new TechnicalManagementException("An error occurs while trying to find a role : " + k, ex);
}
}
);
}
@Override
public List findAll() {
return this.findAllByOrganization(GraviteeContext.getCurrentOrganization());
}
private List findAllByOrganization(String organizationId) {
try {
LOGGER.debug("Find all Roles");
return roleRepository
.findAllByReferenceIdAndReferenceType(organizationId, RoleReferenceType.ORGANIZATION)
.stream()
.map(this::convert)
.collect(toList());
} catch (TechnicalException ex) {
LOGGER.error("An error occurs while trying to find all roles", ex);
throw new TechnicalManagementException("An error occurs while trying to find all roles", ex);
}
}
@Override
public RoleEntity create(final NewRoleEntity roleEntity) {
return this.create(roleEntity, GraviteeContext.getCurrentOrganization());
}
private RoleEntity create(final NewRoleEntity roleEntity, String organizationId) {
try {
Role role = convert(roleEntity);
if (
roleRepository
.findByScopeAndNameAndReferenceIdAndReferenceType(
role.getScope(),
role.getName(),
organizationId,
RoleReferenceType.ORGANIZATION
)
.isPresent()
) {
throw new RoleAlreadyExistsException(role.getScope(), role.getName());
}
role.setId(RandomString.generate());
role.setCreatedAt(new Date());
role.setUpdatedAt(role.getCreatedAt());
role.setReferenceId(organizationId);
role.setReferenceType(RoleReferenceType.ORGANIZATION);
RoleEntity entity = convert(roleRepository.create(role));
auditService.createOrganizationAuditLog(
Collections.singletonMap(ROLE, role.getScope() + ":" + role.getName()),
ROLE_CREATED,
role.getCreatedAt(),
null,
role
);
if (entity.isDefaultRole()) {
toggleDefaultRole(roleEntity.getScope(), entity.getName());
}
return entity;
} catch (TechnicalException ex) {
LOGGER.error("An error occurs while trying to create role {}", roleEntity.getName(), ex);
throw new TechnicalManagementException("An error occurs while trying to create role " + roleEntity.getName(), ex);
}
}
@Override
public RoleEntity update(final UpdateRoleEntity roleEntity) {
if (isReserved(roleEntity.getName())) {
throw new RoleReservedNameException(roleEntity.getName());
}
RoleScope scope = roleEntity.getScope();
try {
Optional optRole = roleRepository.findById(roleEntity.getId());
if (!optRole.isPresent()) {
throw new RoleNotFoundException(roleEntity.getId());
}
Role role = optRole.get();
Role updatedRole = convert(roleEntity);
updatedRole.setCreatedAt(role.getCreatedAt());
updatedRole.setReferenceId(role.getReferenceId());
updatedRole.setReferenceType(role.getReferenceType());
RoleEntity entity = convert(roleRepository.update(updatedRole));
auditService.createOrganizationAuditLog(
Collections.singletonMap(ROLE, role.getScope() + ":" + role.getName()),
ROLE_UPDATED,
updatedRole.getUpdatedAt(),
role,
updatedRole
);
if (entity.isDefaultRole()) {
toggleDefaultRole(scope, entity.getName());
}
return entity;
} catch (TechnicalException ex) {
LOGGER.error("An error occurs while trying to update role {}", roleEntity.getName(), ex);
throw new TechnicalManagementException("An error occurs while trying to update role " + roleEntity.getName(), ex);
}
}
@Override
public void delete(final String roleId) {
try {
Optional optRole = roleRepository.findById(roleId);
if (!optRole.isPresent()) {
throw new RoleNotFoundException(roleId);
}
Role role = optRole.get();
RoleScope scope = convert(role.getScope());
if (role.isDefaultRole() || role.isSystem()) {
throw new RoleDeletionForbiddenException(scope, role.getName());
}
List defaultRoleByScopes = findDefaultRoleByScopes(scope);
if (defaultRoleByScopes.isEmpty()) {
throw new DefaultRoleNotFoundException();
}
membershipService.removeRoleUsage(roleId, defaultRoleByScopes.get(0).getId());
roleRepository.delete(roleId);
auditService.createOrganizationAuditLog(
Collections.singletonMap(ROLE, scope + ":" + role.getName()),
ROLE_DELETED,
role.getUpdatedAt(),
role,
null
);
} catch (TechnicalException ex) {
LOGGER.error("An error occurs while trying to delete role {}", roleId, ex);
throw new TechnicalManagementException("An error occurs while trying to delete role " + roleId, ex);
}
}
@Override
public List findByScope(RoleScope scope) {
try {
LOGGER.debug("Find Roles by scope");
return roleRepository
.findByScopeAndReferenceIdAndReferenceType(
convert(scope),
GraviteeContext.getCurrentOrganization(),
RoleReferenceType.ORGANIZATION
)
.stream()
.map(this::convert)
.sorted(comparing(RoleEntity::getName))
.collect(toList());
} catch (TechnicalException ex) {
LOGGER.error("An error occurs while trying to find roles by scope", ex);
throw new TechnicalManagementException("An error occurs while trying to find roles by scope", ex);
}
}
@Override
public Optional findByScopeAndName(RoleScope scope, String name) {
return this.findByScopeAndName(scope, name, GraviteeContext.getCurrentOrganization());
}
private Optional findByScopeAndName(RoleScope scope, String name, String organizationId) {
try {
LOGGER.debug("Find Roles by scope and name");
Optional optRole = roleRepository.findByScopeAndNameAndReferenceIdAndReferenceType(
convert(scope),
name,
organizationId,
RoleReferenceType.ORGANIZATION
);
if (optRole.isPresent()) {
return Optional.of(this.convert(optRole.get()));
} else {
return Optional.empty();
}
} catch (TechnicalException ex) {
LOGGER.error("An error occurs while trying to find roles by scope", ex);
throw new TechnicalManagementException("An error occurs while trying to find roles by scope", ex);
}
}
@Override
public List findDefaultRoleByScopes(RoleScope... scopes) {
try {
LOGGER.debug("Find default Roles by scope");
List roles = new ArrayList<>();
for (RoleScope scope : scopes) {
roles.addAll(
roleRepository
.findByScopeAndReferenceIdAndReferenceType(
convert(scope),
GraviteeContext.getCurrentOrganization(),
RoleReferenceType.ORGANIZATION
)
.stream()
.filter(Role::isDefaultRole)
.map(this::convert)
.collect(toList())
);
}
return roles;
} catch (TechnicalException ex) {
LOGGER.error("An error occurs while trying to find default roles by scope", ex);
throw new TechnicalManagementException("An error occurs while trying to find default roles by scope", ex);
}
}
@Override
public boolean hasPermission(Map userPermissions, Permission permission, RolePermissionAction[] acls) {
boolean hasPermission = false;
if (userPermissions != null) {
Iterator> it = userPermissions.entrySet().iterator();
while (it.hasNext() && !hasPermission) {
Map.Entry entry = it.next();
if (permission.getName().equals(entry.getKey())) {
String crud = Arrays.toString(entry.getValue());
for (RolePermissionAction perm : acls) {
if (crud.indexOf(perm.getId()) != -1) {
hasPermission = true;
}
}
}
}
}
return hasPermission;
}
private void toggleDefaultRole(RoleScope scope, String newDefaultRoleName) throws TechnicalException {
List roles = roleRepository
.findByScopeAndReferenceIdAndReferenceType(
convert(scope),
GraviteeContext.getCurrentOrganization(),
RoleReferenceType.ORGANIZATION
)
.stream()
.filter(Role::isDefaultRole)
.collect(toList());
for (Role role : roles) {
if (!role.getName().equals(newDefaultRoleName)) {
Role previousRole = new Role(role);
role.setDefaultRole(false);
role.setUpdatedAt(new Date());
roleRepository.update(role);
auditService.createOrganizationAuditLog(
Collections.singletonMap(ROLE, role.getScope() + ":" + role.getName()),
ROLE_UPDATED,
role.getUpdatedAt(),
previousRole,
role
);
}
}
}
private Role convert(final NewRoleEntity roleEntity) {
final Role role = new Role();
role.setName(generateId(roleEntity.getName()));
role.setDescription(roleEntity.getDescription());
role.setScope(convert(roleEntity.getScope()));
role.setDefaultRole(roleEntity.isDefaultRole());
role.setPermissions(convertPermissions(roleEntity.getScope(), roleEntity.getPermissions()));
role.setCreatedAt(new Date());
role.setUpdatedAt(role.getCreatedAt());
return role;
}
private Role convert(final UpdateRoleEntity roleEntity) {
if (roleEntity == null) {
return null;
}
final Role role = new Role();
role.setName(generateId(roleEntity.getName()));
role.setId(roleEntity.getId());
role.setDescription(roleEntity.getDescription());
role.setScope(convert(roleEntity.getScope()));
role.setDefaultRole(roleEntity.isDefaultRole());
role.setPermissions(convertPermissions(roleEntity.getScope(), roleEntity.getPermissions()));
role.setUpdatedAt(new Date());
return role;
}
private RoleEntity convert(final Role role) {
if (role == null) {
return null;
}
final RoleEntity roleEntity = new RoleEntity();
roleEntity.setId(role.getId());
roleEntity.setName(role.getName());
roleEntity.setDescription(role.getDescription());
roleEntity.setScope(convert(role.getScope()));
roleEntity.setDefaultRole(role.isDefaultRole());
roleEntity.setSystem(role.isSystem());
roleEntity.setPermissions(convertPermissions(roleEntity.getScope(), role.getPermissions()));
return roleEntity;
}
private int[] convertPermissions(io.gravitee.rest.api.model.permissions.RoleScope scope, Map perms) {
if (perms == null || perms.isEmpty()) {
return new int[0];
}
int[] result = new int[perms.size()];
int idx = 0;
for (Map.Entry entry : perms.entrySet()) {
int perm = 0;
for (char c : entry.getValue()) {
perm += RolePermissionAction.findById(c).getMask();
}
result[idx++] = Permission.findByScopeAndName(scope, entry.getKey()).getMask() + perm;
}
return result;
}
private Map convertPermissions(io.gravitee.rest.api.model.permissions.RoleScope scope, int[] perms) {
if (perms == null) {
return Collections.emptyMap();
}
Map result = new HashMap<>();
Stream
.of(Permission.findByScope(scope))
.forEach(
perm -> {
for (int action : perms) {
if (action / 100 == perm.getMask() / 100) {
List crud = new ArrayList<>();
for (RolePermissionAction rolePermissionAction : RolePermissionAction.values()) {
if (((action - perm.getMask()) & rolePermissionAction.getMask()) != 0) {
crud.add(rolePermissionAction.getId());
}
}
result.put(perm.getName(), ArrayUtils.toPrimitive(crud.toArray(new Character[crud.size()])));
}
}
}
);
return result;
}
private io.gravitee.repository.management.model.RoleScope convert(RoleScope scope) {
if (scope == null) {
return null;
}
return io.gravitee.repository.management.model.RoleScope.valueOf(scope.name());
}
private RoleScope convert(io.gravitee.repository.management.model.RoleScope scope) {
if (scope == null) {
return null;
}
return RoleScope.valueOf(scope.name());
}
private String generateId(String name) {
String id = name.trim().toUpperCase().replaceAll(" +", " ").replaceAll(" ", "_").replaceAll("[^\\w\\s]", "_").replaceAll("-+", "_");
if (isReserved(id)) {
throw new RoleReservedNameException(id);
}
return id;
}
private boolean isReserved(String name) {
for (SystemRole systemRole : SystemRole.values()) {
if (systemRole.name().equals(name)) {
return true;
}
}
return false;
}
@Override
public void initialize(String organizationId) {
LOGGER.info(" - USER (default)");
this.create(DEFAULT_ROLE_ORGANIZATION_USER, organizationId);
LOGGER.info(" - API_PUBLISHER");
this.create(ROLE_ENVIRONMENT_API_PUBLISHER, organizationId);
LOGGER.info(" - USER (default)");
this.create(DEFAULT_ROLE_ENVIRONMENT_USER, organizationId);
LOGGER.info(" - USER (default)");
this.create(DEFAULT_ROLE_API_USER, organizationId);
LOGGER.info(" - OWNER");
this.create(ROLE_API_OWNER, organizationId);
LOGGER.info(" - REVIEWER");
this.create(ROLE_API_REVIEWER, organizationId);
LOGGER.info(" - USER (default)");
this.create(DEFAULT_ROLE_APPLICATION_USER, organizationId);
LOGGER.info(" - OWNER");
this.create(ROLE_APPLICATION_OWNER, organizationId);
}
@Override
public void createOrUpdateSystemRoles(String organizationId) {
try {
//ORGANIZATION - ADMIN
createOrUpdateSystemRole(SystemRole.ADMIN, RoleScope.ORGANIZATION, OrganizationPermission.values(), organizationId);
//ENVIRONMENT - ADMIN
createOrUpdateSystemRole(SystemRole.ADMIN, RoleScope.ENVIRONMENT, EnvironmentPermission.values(), organizationId);
//API - PRIMARY_OWNER
createOrUpdateSystemRole(
SystemRole.PRIMARY_OWNER,
RoleScope.API,
stream(ApiPermission.values()).filter(permission -> !REVIEWS.equals(permission)).toArray(Permission[]::new),
organizationId
);
//APPLICATION - PRIMARY_OWNER
createOrUpdateSystemRole(SystemRole.PRIMARY_OWNER, RoleScope.APPLICATION, ApplicationPermission.values(), organizationId);
//GROUP - ADMINISTRATOR
createOrUpdateSystemRole(SystemRole.ADMIN, RoleScope.GROUP, GroupPermission.values(), organizationId);
} catch (TechnicalException ex) {
LOGGER.error("An error occurs while trying to create admin roles", ex);
throw new TechnicalManagementException("An error occurs while trying to create admin roles ", ex);
}
}
@Override
public RoleScope findScopeByMembershipReferenceType(MembershipReferenceType type) {
switch (type) {
case API:
return RoleScope.API;
case GROUP:
return RoleScope.GROUP;
case APPLICATION:
return RoleScope.APPLICATION;
case ENVIRONMENT:
return RoleScope.ENVIRONMENT;
case ORGANIZATION:
return RoleScope.ORGANIZATION;
case PLATFORM:
return RoleScope.PLATFORM;
}
return null;
}
@Override
public RoleEntity findPrimaryOwnerRoleByOrganization(String organizationId, RoleScope roleScope) {
if (roleScope == RoleScope.API) {
return apiPrimaryOwnersByOrganization.computeIfAbsent(
organizationId,
s -> this.findByScopeAndName(RoleScope.API, SystemRole.PRIMARY_OWNER.name(), s).get()
);
}
if (roleScope == RoleScope.APPLICATION) {
return applicationPrimaryOwnersByOrganization.computeIfAbsent(
organizationId,
s -> this.findByScopeAndName(RoleScope.APPLICATION, SystemRole.PRIMARY_OWNER.name(), s).get()
);
}
throw new RoleNotFoundException(roleScope + "_PRIMARY_OWNER");
}
private void createOrUpdateSystemRole(SystemRole roleName, RoleScope roleScope, Permission[] permissions, String organizationId)
throws TechnicalException {
Role systemRole = createSystemRoleWithoutPermissions(roleName.name(), roleScope, new Date());
Map perms = new HashMap<>();
for (Permission perm : permissions) {
perms.put(perm.getName(), new char[] { CREATE.getId(), READ.getId(), UPDATE.getId(), DELETE.getId() });
}
systemRole.setPermissions(convertPermissions(roleScope, perms));
systemRole.setReferenceId(organizationId);
systemRole.setReferenceType(RoleReferenceType.ORGANIZATION);
Optional existingRole = roleRepository.findByScopeAndNameAndReferenceIdAndReferenceType(
systemRole.getScope(),
systemRole.getName(),
GraviteeContext.getCurrentOrganization(),
RoleReferenceType.ORGANIZATION
);
if (existingRole.isPresent() && permissionsAreDifferent(existingRole.get(), systemRole)) {
systemRole.setId(existingRole.get().getId());
systemRole.setUpdatedAt(new Date());
roleRepository.update(systemRole);
auditService.createOrganizationAuditLog(
Collections.singletonMap(ROLE, systemRole.getScope() + ":" + systemRole.getName()),
ROLE_UPDATED,
systemRole.getCreatedAt(),
existingRole,
systemRole
);
} else if (!existingRole.isPresent()) {
roleRepository.create(systemRole);
auditService.createOrganizationAuditLog(
Collections.singletonMap(ROLE, systemRole.getScope() + ":" + systemRole.getName()),
ROLE_CREATED,
systemRole.getCreatedAt(),
null,
systemRole
);
}
}
private Role createSystemRoleWithoutPermissions(String name, RoleScope scope, Date date) {
LOGGER.info(" - <" + scope + "> " + name + " (system)");
Role systemRole = new Role();
systemRole.setId(RandomString.generate());
systemRole.setName(name);
systemRole.setDescription("System Role. Created by Gravitee.io");
systemRole.setDefaultRole(false);
systemRole.setSystem(true);
systemRole.setScope(convert(scope));
systemRole.setCreatedAt(date);
systemRole.setUpdatedAt(date);
return systemRole;
}
private boolean permissionsAreDifferent(Role role1, Role role2) {
return (
stream(role1.getPermissions()).reduce(Math::addExact).orElse(0) !=
stream(role2.getPermissions()).reduce(Math::addExact).orElse(0)
);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy