Skip to content

Commit

Permalink
feat: roles and permissions in identity backend (#19514)
Browse files Browse the repository at this point in the history
## Description
* roles CRUD in identity backend

## Related issues

closes https://github.com/camunda-cloud/identity/issues/2888
  • Loading branch information
Steffen Pade committed Jun 21, 2024
2 parents 19b888d + 7e01af5 commit 3540a9e
Show file tree
Hide file tree
Showing 10 changed files with 453 additions and 1 deletion.
14 changes: 13 additions & 1 deletion identity/backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,18 @@
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<!-- Provided dependencies -->
<dependency>
<groupId>jakarta.servlet</groupId>
Expand All @@ -119,7 +127,11 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit-jupiter-api.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-shared-utils</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
* one or more contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright ownership.
* Licensed under the Camunda License 1.0. You may not use this file
* except in compliance with the Camunda License 1.0.
*/
package io.camunda.identity.rolemanagement.model;

import io.camunda.identity.permissions.PermissionEnum;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "roles")
public class Role {

@Id
@NotBlank(message = "role.notValid")
@Column(name = "authority")
private String name;

private String description;

@NotNull(message = "role.notValid")
@ElementCollection(targetClass = PermissionEnum.class, fetch = FetchType.EAGER)
@JoinTable(name = "role_permissions", joinColumns = @JoinColumn(name = "role_authority"))
@Column(name = "permission", nullable = false)
@Enumerated(EnumType.STRING)
private Set<PermissionEnum> permissions = new HashSet<>();

public String getName() {
return name;
}

public void setName(final String name) {
this.name = name;
}

public String getDescription() {
return description;
}

public void setDescription(final String description) {
this.description = description;
}

public Set<PermissionEnum> getPermissions() {
return permissions;
}

public void setPermissions(final Set<PermissionEnum> permissions) {
this.permissions = permissions;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
* one or more contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright ownership.
* Licensed under the Camunda License 1.0. You may not use this file
* except in compliance with the Camunda License 1.0.
*/
package io.camunda.identity.rolemanagement.repository;

import io.camunda.identity.rolemanagement.model.Role;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface RoleRepository extends JpaRepository<Role, String> {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
* one or more contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright ownership.
* Licensed under the Camunda License 1.0. You may not use this file
* except in compliance with the Camunda License 1.0.
*/
package io.camunda.identity.rolemanagement.service;

import io.camunda.identity.rolemanagement.model.Role;
import io.camunda.identity.rolemanagement.repository.RoleRepository;
import jakarta.validation.Valid;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

@Service
@Validated
@Transactional
public class RoleService {
private final RoleRepository roleRepository;

public RoleService(final RoleRepository roleRepository) {
this.roleRepository = roleRepository;
}

public Role createRole(@Valid final Role role) {
if (role.getPermissions() == null) {
role.setPermissions(new HashSet<>());
}

return roleRepository.save(role);
}

public void deleteRoleByName(final String roleName) {
roleRepository.deleteById(roleName);
}

public Role findRoleByName(final String roleName) {
return roleRepository
.findById(roleName)
.orElseThrow(() -> new RuntimeException("role.notFound"));
}

public List<Role> findAllRoles() {
return roleRepository.findAll();
}

public Role updateRole(final String roleName, @Valid final Role role) {
if (!Objects.equals(roleName, role.getName())) {
throw new RuntimeException("role.notFound");
}

final Role existingRole = findRoleByName(roleName);

existingRole.setDescription(role.getDescription());
existingRole.setPermissions(role.getPermissions());

return roleRepository.save(existingRole);
}
}
14 changes: 14 additions & 0 deletions identity/backend/src/main/resources/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ CREATE TABLE IF NOT EXISTS users (
enabled boolean not null
);

-- authorities == roles
CREATE TABLE IF NOT EXISTS authorities (
username varchar not null,
authority varchar not null,
Expand All @@ -33,6 +34,7 @@ CREATE TABLE IF NOT EXISTS groups (
organization_id varchar(250) null
);

-- group roles
CREATE TABLE IF NOT EXISTS group_authorities (
group_id bigint not null,
authority varchar(50) not null,
Expand All @@ -50,3 +52,15 @@ CREATE TABLE IF NOT EXISTS group_members (
CREATE TABLE IF NOT EXISTS profiles(
id bigint primary key, email varchar(100) unique,
constraint fk_profile_users foreign key(id) references users(id) ON DELETE CASCADE);

-- authority == role
CREATE TABLE IF NOT EXISTS roles(
authority varchar(50) not null primary key,
description varchar(500)
);

CREATE TABLE IF NOT EXISTS role_permissions(
role_authority varchar(50) not null,
permission varchar(50) not null,
constraint unique_rp unique(role_authority, permission)
);
53 changes: 53 additions & 0 deletions identity/backend/src/test/java/io/camunda/identity/TestHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
* one or more contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright ownership.
* Licensed under the Camunda License 1.0. You may not use this file
* except in compliance with the Camunda License 1.0.
*/
package io.camunda.identity;

import io.camunda.identity.rolemanagement.model.Role;
import io.camunda.identity.rolemanagement.service.RoleService;
import io.camunda.identity.usermanagement.CamundaGroup;
import io.camunda.identity.usermanagement.CamundaUser;
import io.camunda.identity.usermanagement.CamundaUserWithPassword;
import io.camunda.identity.usermanagement.service.GroupService;
import io.camunda.identity.usermanagement.service.UserService;
import org.apache.maven.surefire.shared.lang3.RandomStringUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

public class TestHelper {

public static final String DEFAULT_USER_ROLE = "DEFAULT_USER";

public static CamundaUser createAndSaveRandomUser(final UserService userService) {
return userService.createUser(
new CamundaUserWithPassword(
RandomStringUtils.randomAlphabetic(10), "email", false, "password"));
}

public static CamundaGroup createAndSaveRandomGroup(final GroupService groupService) {
return groupService.createGroup(new CamundaGroup(RandomStringUtils.randomAlphabetic(10)));
}

public static Role createAndSaveRandomRole(final RoleService roleService) {
final String roleName = RandomStringUtils.randomAlphabetic(10);
final String description = RandomStringUtils.randomAlphabetic(15);

final Role role = new Role();
role.setName(roleName);
role.setDescription(description);

return roleService.createRole(role);
}

public static String buildPrefixedRoleName(final String roleName) {
return String.format("ROLE_%s", roleName);
}

public static SimpleGrantedAuthority buildSimpleGrantedAuthorityFromRoleName(
final String roleName) {
return new SimpleGrantedAuthority(buildPrefixedRoleName(roleName));
}
}
Loading

0 comments on commit 3540a9e

Please sign in to comment.