-
Notifications
You must be signed in to change notification settings - Fork 218
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2028 from akto-api-security/feature/custom_roles_2
Feature/custom roles 2
- Loading branch information
Showing
33 changed files
with
1,205 additions
and
184 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
210 changes: 210 additions & 0 deletions
210
apps/dashboard/src/main/java/com/akto/action/RoleAction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
package com.akto.action; | ||
|
||
import java.util.List; | ||
|
||
import com.akto.dao.CustomRoleDao; | ||
import com.akto.dao.PendingInviteCodesDao; | ||
import com.akto.dao.RBACDao; | ||
import com.akto.dao.context.Context; | ||
import com.akto.dto.CustomRole; | ||
import com.akto.dto.PendingInviteCode; | ||
import com.akto.dto.RBAC; | ||
import com.akto.dto.RBAC.Role; | ||
import com.akto.util.Pair; | ||
import com.mongodb.BasicDBObject; | ||
import com.mongodb.client.model.Filters; | ||
import com.mongodb.client.model.Updates; | ||
|
||
public class RoleAction extends UserAction { | ||
|
||
/* | ||
* Create Role. | ||
* Update Role. | ||
* Delete Role. -> If no user is associated with the role. | ||
* Get Roles. | ||
*/ | ||
|
||
List<CustomRole> roles; | ||
|
||
public List<CustomRole> getRoles() { | ||
return roles; | ||
} | ||
|
||
public String getCustomRoles() { | ||
/* | ||
* Need all data for a role, | ||
* thus no projections being used. | ||
*/ | ||
roles = CustomRoleDao.instance.findAll(new BasicDBObject()); | ||
return SUCCESS.toUpperCase(); | ||
} | ||
|
||
List<Integer> apiCollectionIds; | ||
|
||
public void setApiCollectionIds(List<Integer> apiCollectionIds) { | ||
this.apiCollectionIds = apiCollectionIds; | ||
} | ||
|
||
String roleName; | ||
|
||
public void setRoleName(String roleName) { | ||
this.roleName = roleName; | ||
} | ||
|
||
String baseRole; | ||
|
||
public void setBaseRole(String baseRole) { | ||
this.baseRole = baseRole; | ||
} | ||
|
||
boolean defaultInviteRole; | ||
|
||
public void setDefaultInviteRole(boolean defaultInviteRole) { | ||
this.defaultInviteRole = defaultInviteRole; | ||
} | ||
|
||
private static final int MAX_ROLE_NAME_LENGTH = 50; | ||
|
||
public boolean validateRoleName() { | ||
if (this.roleName == null || this.roleName.isEmpty()) { | ||
addActionError("Role names cannot be empty."); | ||
return false; | ||
} | ||
|
||
if (this.roleName.length() > MAX_ROLE_NAME_LENGTH) { | ||
addActionError("Role names cannot be more than " + MAX_ROLE_NAME_LENGTH + " characters."); | ||
return false; | ||
} | ||
|
||
for (char c : this.roleName.toCharArray()) { | ||
boolean alphabets = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); | ||
boolean numbers = c >= '0' && c <= '9'; | ||
boolean specialChars = c == '-' || c == '_'; | ||
|
||
if (!(alphabets || numbers || specialChars)) { | ||
addActionError("Role names can only be alphanumeric and contain '-'and '_'"); | ||
return false; | ||
} | ||
} | ||
|
||
try { | ||
/* | ||
* We do not want role name from the reserved names. | ||
*/ | ||
Role.valueOf(this.roleName); | ||
addActionError(this.roleName + " is a reserved keyword."); | ||
return false; | ||
} catch(Exception e){ | ||
} | ||
|
||
return true; | ||
} | ||
|
||
private boolean defaultInviteCheck(){ | ||
if(defaultInviteRole){ | ||
List<CustomRole> roles = CustomRoleDao.instance.findAll(new BasicDBObject()); | ||
for(CustomRole role: roles){ | ||
if(role.getDefaultInviteRole()){ | ||
addActionError("Default invite role already exists."); | ||
return false; | ||
} | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
public String createCustomRole() { | ||
|
||
if (!validateRoleName()) { | ||
return ERROR.toUpperCase(); | ||
} | ||
|
||
// Always save Upper-case. | ||
roleName = roleName.toUpperCase(); | ||
|
||
CustomRole existingRole = CustomRoleDao.instance.findRoleByName(roleName); | ||
|
||
if (existingRole != null) { | ||
addActionError("Existing role with same name exists."); | ||
return ERROR.toUpperCase(); | ||
} | ||
try { | ||
Role.valueOf(baseRole); | ||
} catch (Exception e) { | ||
addActionError("Base role does not exist"); | ||
return ERROR.toUpperCase(); | ||
} | ||
|
||
if(!defaultInviteCheck()){ | ||
return ERROR.toUpperCase(); | ||
} | ||
|
||
CustomRole role = new CustomRole(roleName, baseRole, apiCollectionIds, defaultInviteRole); | ||
CustomRoleDao.instance.insertOne(role); | ||
RBACDao.instance.deleteUserEntryFromCache(new Pair<>(getSUser().getId(), Context.accountId.get())); | ||
return SUCCESS.toUpperCase(); | ||
} | ||
|
||
public String updateCustomRole(){ | ||
if (!validateRoleName()) { | ||
return ERROR.toUpperCase(); | ||
} | ||
CustomRole existingRole = CustomRoleDao.instance.findRoleByName(roleName); | ||
|
||
if (existingRole == null) { | ||
addActionError("Role does not exist."); | ||
return ERROR.toUpperCase(); | ||
} | ||
|
||
try { | ||
Role.valueOf(baseRole); | ||
} catch (Exception e) { | ||
addActionError("Base role does not exist"); | ||
return ERROR.toUpperCase(); | ||
} | ||
|
||
if(!defaultInviteCheck() && !existingRole.getDefaultInviteRole()){ | ||
return ERROR.toUpperCase(); | ||
} | ||
|
||
CustomRoleDao.instance.updateOne(Filters.eq(CustomRole._NAME, roleName),Updates.combine( | ||
Updates.set(CustomRole.BASE_ROLE, baseRole), | ||
Updates.set(CustomRole.API_COLLECTIONS_ID, apiCollectionIds), | ||
Updates.set(CustomRole.DEFAULT_INVITE_ROLE, defaultInviteRole) | ||
)); | ||
RBACDao.instance.deleteUserEntryFromCache(new Pair<>(getSUser().getId(), Context.accountId.get())); | ||
|
||
return SUCCESS.toUpperCase(); | ||
} | ||
|
||
public String deleteCustomRole(){ | ||
CustomRole existingRole = CustomRoleDao.instance.findRoleByName(roleName); | ||
|
||
if (existingRole == null) { | ||
addActionError("Role does not exist."); | ||
return ERROR.toUpperCase(); | ||
} | ||
|
||
List<RBAC> usersWithRole = RBACDao.instance.findAll(Filters.eq(RBAC.ROLE, roleName)); | ||
|
||
if(!usersWithRole.isEmpty()){ | ||
addActionError("Role is associated with users. Cannot delete."); | ||
return ERROR.toUpperCase(); | ||
} | ||
|
||
/* | ||
* Alt. approach: Delete all pending invites associated with the role. | ||
*/ | ||
List<PendingInviteCode> pendingInviteCodes = PendingInviteCodesDao.instance.findAll(Filters.eq(PendingInviteCode.INVITEE_ROLE, roleName)); | ||
if(!pendingInviteCodes.isEmpty()){ | ||
addActionError("Role is associated with pending invites. Cannot delete."); | ||
return ERROR.toUpperCase(); | ||
} | ||
|
||
CustomRoleDao.instance.deleteAll(Filters.eq(CustomRole._NAME, roleName)); | ||
RBACDao.instance.deleteUserEntryFromCache(new Pair<>(getSUser().getId(), Context.accountId.get())); | ||
|
||
return SUCCESS.toUpperCase(); | ||
} | ||
|
||
} |
Oops, something went wrong.