Skip to content

Commit

Permalink
SW-4324 Add user to any organization from admin UI (#1425)
Browse files Browse the repository at this point in the history
Allow super-admins to add a user to any organization from the admin UI.

This replaces the previous UI for adding a Terraformation contact, which required
the super-admin to be in the organization.
  • Loading branch information
sgrimm authored Oct 24, 2023
1 parent 6af546e commit 7c9a28a
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.terraformation.backend.db.default_schema.FacilityType
import com.terraformation.backend.db.default_schema.InternalTagId
import com.terraformation.backend.db.default_schema.OrganizationId
import com.terraformation.backend.db.default_schema.ReportId
import com.terraformation.backend.db.default_schema.Role
import com.terraformation.backend.db.default_schema.SubLocationId
import com.terraformation.backend.db.default_schema.UserId
import com.terraformation.backend.db.default_schema.UserType
Expand Down Expand Up @@ -149,13 +150,17 @@ class AdminController(
@GetMapping("/")
fun getIndex(model: Model): String {
val organizations = organizationStore.fetchAll().sortedBy { it.id.value }
val allOrganizations = organizationsDao.findAll().sortedBy { it.id!!.value }

model.addAttribute("allOrganizations", allOrganizations)
model.addAttribute("canAddAnyOrganizationUser", currentUser().canAddAnyOrganizationUser())
model.addAttribute("canImportGlobalSpeciesData", currentUser().canImportGlobalSpeciesData())
model.addAttribute("canManageInternalTags", currentUser().canManageInternalTags())
model.addAttribute("canSetTestClock", config.useTestClock && currentUser().canSetTestClock())
model.addAttribute("canUpdateAppVersions", currentUser().canUpdateAppVersions())
model.addAttribute("organizations", organizations)
model.addAttribute("prefix", prefix)
model.addAttribute("roles", Role.entries.map { it to it.getDisplayName(Locale.ENGLISH) })

return "/admin/index"
}
Expand Down Expand Up @@ -1204,24 +1209,32 @@ class AdminController(
return organization(organizationId)
}

@PostMapping("/assignTerraformationContact")
fun assignTerraformationContact(
@PostMapping("/addOrganizationUser")
fun addOrganizationUser(
@RequestParam organizationId: OrganizationId,
@NotBlank @RequestParam terraformationContactEmail: String,
@NotBlank @RequestParam email: String,
@RequestParam role: Role,
redirectAttributes: RedirectAttributes,
): String {
try {
val metadata =
organizationService.assignTerraformationContact(
terraformationContactEmail, organizationId)
redirectAttributes.successMessage =
"User $metadata assigned as Terraformation Contact in organization $organizationId."
if (role == Role.TerraformationContact) {
val userId = organizationService.assignTerraformationContact(email, organizationId)
redirectAttributes.successMessage =
"User $userId assigned as contact for organization $organizationId"
} else {
if (userStore.fetchByEmail(email) != null) {
val userId = organizationService.addUser(email, organizationId, role)
redirectAttributes.successMessage = "User $userId added to organization $organizationId"
} else {
redirectAttributes.failureMessage = "User $email does not exist"
}
}
} catch (e: Exception) {
log.warn("Terraformation Contact assignment failed", e)
redirectAttributes.failureMessage = "Terraformation Contact assignment failed: ${e.message}"
log.warn("Failed to add user to organization", e)
redirectAttributes.failureMessage = "Adding user failed: ${e.message}"
}

return organization(organizationId)
return adminHome()
}

@PostMapping("/deleteReport")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ data class DeviceManagerUser(
organizationId in permissionStore.fetchOrganizationRoles(targetUserId)
}

override fun canAddAnyOrganizationUser(): Boolean = false
override fun canAddOrganizationUser(organizationId: OrganizationId): Boolean = false
override fun canAddTerraformationContact(organizationId: OrganizationId): Boolean = false
override fun canCountNotifications(): Boolean = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,10 @@ data class IndividualUser(
override fun isCredentialsNonExpired() = true
override fun isEnabled() = true

override fun canAddAnyOrganizationUser() = isSuperAdmin()

override fun canAddOrganizationUser(organizationId: OrganizationId) =
isAdminOrHigher(organizationId)
isSuperAdmin() || isAdminOrHigher(organizationId)

override fun canAddTerraformationContact(organizationId: OrganizationId) = isSuperAdmin()

Expand Down Expand Up @@ -346,7 +348,7 @@ data class IndividualUser(
override fun canSendAlert(facilityId: FacilityId) = isAdminOrHigher(facilityId)

override fun canSetOrganizationUserRole(organizationId: OrganizationId, role: Role) =
isAdminOrHigher(organizationId)
isSuperAdmin() || isAdminOrHigher(organizationId)

override fun canSetTerraformationContact(organizationId: OrganizationId) = isSuperAdmin()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ class SystemUser(
* manually by a system administrator.
*/

override fun canAddAnyOrganizationUser(): Boolean = true
override fun canAddOrganizationUser(organizationId: OrganizationId): Boolean = true
override fun canAddTerraformationContact(organizationId: OrganizationId): Boolean = true
override fun canCountNotifications(): Boolean = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ interface TerrawareUser : Principal {
* Permission checks. Each of these returns true if the user has permission to perform the action.
*/

fun canAddAnyOrganizationUser(): Boolean
fun canAddOrganizationUser(organizationId: OrganizationId): Boolean
fun canAddTerraformationContact(organizationId: OrganizationId): Boolean
fun canCountNotifications(): Boolean
Expand Down
30 changes: 30 additions & 0 deletions src/main/resources/templates/admin/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,35 @@ <h2>Internal Tags</h2>
</p>
</th:block>

<th:block th:if="${canAddAnyOrganizationUser}">
<h2>Add Organization User</h2>

<p>
If the role is "Terraformation Contact" and the organization already has a Terraformation
contact, the existing contact will be replaced by the new one.
</p>

<form method="POST" th:action="|${prefix}/addOrganizationUser|">
<label for="addUserEmail">Email (user must already exist)</label>
<input id="addUserEmail" type="email" name="email" required />
<label for="addUserOrganizationId">Organization</label>
<select id="addUserOrganizationId" name="organizationId">
<option th:each="organization : ${allOrganizations}" th:value="${organization.id}"
th:text="|${organization.name} (${organization.id})|">
My Org (123)
</option>
</select>
<label for="addUserRole">Role</label>
<select id="addUserRole" name="role">
<option th:each="role : ${roles}"
th:value="${role.first}"
th:text="${role.second}"
th:selected="${role.second} == 'Admin'">
Some Role
</option>
</select>
<input type="submit" value="Add User"/>
</form>
</th:block>
</body>
</html>
18 changes: 0 additions & 18 deletions src/main/resources/templates/admin/organization.html
Original file line number Diff line number Diff line change
Expand Up @@ -354,23 +354,5 @@ <h4>Create Report (for development testing)</h4>
document.getElementById('showMap').addEventListener('click', showMap);
</script>

<hr style="margin-top: 20px"/>

<h3>Terraformation Contact</h3>
<span th:text="|Current Terraformation Contact: ${terraformationContact != null ? terraformationContact.email : 'none'}|">Terraformation Contact</span>

<form method="POST" th:action="|${prefix}/assignTerraformationContact|" th:if="${canAssignTerraformationContact}">
<h4>Assign New Terraformation Contact (interim solution - until we add TW support)</h4>
<p>
The current Terraformation Contact will be removed from the org (if one exists) and the new one will be assigned.
</p>
<label for="email" style="margin-top: 20px">Enter the Terraformation Contact's <i>(@terraformation.com)</i> email:</label>
<input type="email" id="terraformationContactEmail" name="terraformationContactEmail" style="min-width: 300px; padding: 2px;" pattern="^\S+@terraformation\.com$">
<input type="hidden" name="organizationId" th:value="${organization.id}"/>
<input type="submit" value=" Assign Terraformation Contact "/>
</form>

<hr style="margin-top: 20px"/>

</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,7 @@ internal class PermissionTest : DatabaseTest() {
)

permissions.expect(
addAnyOrganizationUser = true,
createDeviceManager = true,
manageNotifications = true,
setTestClock = true,
Expand Down Expand Up @@ -1177,6 +1178,7 @@ internal class PermissionTest : DatabaseTest() {
)

permissions.expect(
addAnyOrganizationUser = true,
createDeviceManager = true,
deleteSelf = true,
importGlobalSpeciesData = true,
Expand Down Expand Up @@ -1570,6 +1572,7 @@ internal class PermissionTest : DatabaseTest() {

/** Checks for globally-scoped permissions. */
fun expect(
addAnyOrganizationUser: Boolean = false,
createDeviceManager: Boolean = false,
deleteSelf: Boolean = false,
importGlobalSpeciesData: Boolean = false,
Expand All @@ -1580,6 +1583,8 @@ internal class PermissionTest : DatabaseTest() {
updateAppVersions: Boolean = false,
updateDeviceTemplates: Boolean = false,
) {
assertEquals(
addAnyOrganizationUser, user.canAddAnyOrganizationUser(), "Can add any organization user")
assertEquals(createDeviceManager, user.canCreateDeviceManager(), "Can create device manager")
assertEquals(deleteSelf, user.canDeleteSelf(), "Can delete self")
assertEquals(
Expand Down

0 comments on commit 7c9a28a

Please sign in to comment.