-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mrc 5295- Role create endpoint #62
Changes from all commits
66c123a
82d3a52
2d15547
409d6c9
c9ac5ae
cd043ec
b0c7348
f67fbe4
d4918d4
c0cda7f
cfac8b5
74afb92
82800ef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package packit.controllers | ||
|
||
import org.springframework.http.ResponseEntity | ||
import org.springframework.security.access.prepost.PreAuthorize | ||
import org.springframework.stereotype.Controller | ||
import org.springframework.validation.annotation.Validated | ||
import org.springframework.web.bind.annotation.PostMapping | ||
import org.springframework.web.bind.annotation.RequestBody | ||
import org.springframework.web.bind.annotation.RequestMapping | ||
import packit.model.dto.CreateRole | ||
import packit.service.RoleService | ||
|
||
@Controller | ||
@PreAuthorize("hasAuthority('user.manage')") | ||
@RequestMapping("/role") | ||
class RoleController(private val roleService: RoleService) | ||
{ | ||
@PostMapping() | ||
fun createRole(@RequestBody @Validated createRole: CreateRole): ResponseEntity<Map<String, String?>> | ||
{ | ||
roleService.createRole(createRole) | ||
|
||
return ResponseEntity.ok(mapOf("message" to "Role created")) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,8 +25,7 @@ class Packet( | |
inverseJoinColumns = [JoinColumn(name = "tag_id")] | ||
) | ||
var tags: MutableList<Tag> = mutableListOf(), | ||
|
||
@OneToMany(mappedBy = "packet") | ||
@OneToMany(mappedBy = "packet", cascade = [CascadeType.ALL]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure But even so, I guess it's useful to include it here so that we get the cascade functionality on delete? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah it needs to be there because there a FK from the role_permissions table to the packet table... |
||
var rolePermissions: MutableList<RolePermission> = mutableListOf() | ||
) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package packit.model.dto | ||
|
||
import org.jetbrains.annotations.NotNull | ||
|
||
data class CreateRole( | ||
@field:NotNull | ||
val name: String, | ||
val permissionNames: List<String> = listOf() | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package packit.model | ||
package packit.model.dto | ||
|
||
import org.jetbrains.annotations.NotNull | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package packit.model | ||
package packit.model.dto | ||
|
||
import org.jetbrains.annotations.NotNull | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package packit.service | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there's an inconsistency in module naming creeping in here, as you've got There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh yeah didnt even notice that for packet one must've been old... but yup il update this |
||
|
||
import org.springframework.http.HttpStatus | ||
import org.springframework.stereotype.Service | ||
import packit.exceptions.PackitException | ||
import packit.model.Permission | ||
import packit.repository.PermissionRepository | ||
|
||
interface PermissionService | ||
{ | ||
fun checkMatchingPermissions(permissionsToCheck: List<String>): List<Permission> | ||
} | ||
|
||
@Service | ||
class BasePermissionService( | ||
private val permissionRepository: PermissionRepository | ||
) : PermissionService | ||
{ | ||
override fun checkMatchingPermissions(permissionsToCheck: List<String>): List<Permission> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this method is literally just checking that the basic permissions themselves exist in the database? We're not checking that a user has sufficient permissions to do anything? So we can rely on the result count since we know that permission names are enforced as unique? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yup yup exactly |
||
{ | ||
val matchedPermissions = permissionRepository.findByNameIn(permissionsToCheck) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this case sensitive? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yup will be |
||
|
||
if (matchedPermissions.size != permissionsToCheck.size) | ||
{ | ||
throw PackitException("invalidPermissionsProvided", HttpStatus.BAD_REQUEST) | ||
} | ||
return matchedPermissions | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,22 +5,25 @@ import org.springframework.security.core.GrantedAuthority | |
import org.springframework.security.core.authority.SimpleGrantedAuthority | ||
import org.springframework.stereotype.Service | ||
import packit.exceptions.PackitException | ||
import packit.model.Permission | ||
import packit.model.Role | ||
import packit.model.RolePermission | ||
import packit.model.dto.CreateRole | ||
import packit.repository.RoleRepository | ||
|
||
interface RoleService | ||
{ | ||
fun getUsernameRole(username: String): Role | ||
fun getAdminRole(): Role | ||
fun saveRole(roleName: String) | ||
fun checkMatchingRoles(rolesToCheck: List<String>): List<Role> | ||
fun getGrantedAuthorities(roles: List<Role>): MutableList<GrantedAuthority> | ||
fun createRole(createRole: CreateRole) | ||
} | ||
|
||
@Service | ||
class BaseRoleService( | ||
private val roleRepository: RoleRepository | ||
private val roleRepository: RoleRepository, | ||
private val permissionService: PermissionService | ||
) : RoleService | ||
{ | ||
override fun getUsernameRole(username: String): Role | ||
|
@@ -40,26 +43,34 @@ class BaseRoleService( | |
?: throw PackitException("adminRoleNotFound", HttpStatus.INTERNAL_SERVER_ERROR) | ||
} | ||
|
||
override fun saveRole(roleName: String) | ||
override fun createRole(createRole: CreateRole) | ||
{ | ||
val permissions = permissionService.checkMatchingPermissions(createRole.permissionNames) | ||
|
||
saveRole(createRole.name, permissions) | ||
} | ||
|
||
internal fun saveRole(roleName: String, permissions: List<Permission> = listOf()) | ||
{ | ||
if (roleRepository.existsByName(roleName)) | ||
{ | ||
throw PackitException("roleAlreadyExists") | ||
} | ||
val role = Role(name = roleName) | ||
role.rolePermissions = permissions.map { RolePermission(permission = it, role = role) } | ||
.toMutableList() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah i wish but cant because its a mutable list.. needs to be mutable because we will be updating the list (adding/ removing)... |
||
roleRepository.save(role) | ||
} | ||
|
||
override fun checkMatchingRoles(rolesToCheck: List<String>): List<Role> | ||
{ | ||
val allRoles = roleRepository.findAll() | ||
val foundRoles = rolesToCheck.mapNotNull { name -> allRoles.find { it.name == name } } | ||
val matchedRoles = roleRepository.findByNameIn(rolesToCheck) | ||
|
||
if (foundRoles.size != rolesToCheck.size) | ||
if (matchedRoles.size != rolesToCheck.size) | ||
{ | ||
throw PackitException("invalidRolesProvided", HttpStatus.BAD_REQUEST) | ||
} | ||
return foundRoles | ||
return matchedRoles | ||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package packit.integration.controllers | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import org.junit.jupiter.api.Test | ||
import org.springframework.beans.factory.annotation.Autowired | ||
import org.springframework.http.HttpStatus | ||
import org.springframework.test.context.jdbc.Sql | ||
import packit.integration.IntegrationTest | ||
import packit.integration.WithAuthenticatedUser | ||
import packit.model.dto.CreateRole | ||
import packit.repository.RoleRepository | ||
import kotlin.test.assertEquals | ||
import kotlin.test.assertNotNull | ||
|
||
@Sql("/delete-test-users.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) | ||
class RoleControllerTest : IntegrationTest() | ||
{ | ||
@Autowired | ||
private lateinit var roleRepository: RoleRepository | ||
private val createTestRoleBody = ObjectMapper().writeValueAsString( | ||
CreateRole( | ||
name = "testRole", | ||
permissionNames = listOf("packet.run", "packet.read") | ||
) | ||
) | ||
|
||
@Test | ||
@WithAuthenticatedUser(authorities = ["user.manage"]) | ||
fun `users with manage authority can create roles`() | ||
{ | ||
val result = restTemplate.postForEntity( | ||
"/role", | ||
getTokenizedHttpEntity(data = createTestRoleBody), | ||
String::class.java | ||
) | ||
|
||
assertSuccess(result) | ||
assertNotNull(roleRepository.findByName("testRole")) | ||
} | ||
|
||
@Test | ||
@WithAuthenticatedUser(authorities = ["none"]) | ||
fun `user without user manage permission cannot create roles`() | ||
{ | ||
val result = restTemplate.postForEntity( | ||
"/role", | ||
getTokenizedHttpEntity(data = createTestRoleBody), | ||
String::class.java | ||
) | ||
|
||
assertEquals(result.statusCode, HttpStatus.UNAUTHORIZED) | ||
} | ||
|
||
@Test | ||
@WithAuthenticatedUser(authorities = ["user.manage"]) | ||
fun `reject request if createRole body is invalid`() | ||
{ | ||
val result = restTemplate.postForEntity( | ||
"/role", | ||
getTokenizedHttpEntity(data = "{}"), | ||
String::class.java | ||
) | ||
|
||
assertEquals(result.statusCode, HttpStatus.BAD_REQUEST) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how useful
{"message": "Role created"}
is really. It might be nice to actually just return the new Role, since the front end is probably going to want to display it immediately. Or else just return an empty 200 and let the front end decide what message it wants to show.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup agree.. have created dtos needed for this in user endpoints so for now have created ticket https://mrc-ide.myjetbrains.com/youtrack/issue/mrc-5324/Create-endpoints-to-return-Dtos... this will get done after endpoints tickets