Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 131 additions & 1 deletion docs/content/development/extensions-core/druid-basic-security.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,87 @@ Return a list of all user names.
`GET(/druid-ext/basic-security/authorization/db/{authorizerName}/users/{userName})`
Return the name and role information of the user with name {userName}

Example output:
```json
{
"name": "druid2",
"roles": [
"druidRole"
]
}
```

This API supports the following flags:
- `?full`: The response will also include the full information for each role currently assigned to the user.

Example output:
```json
{
"name": "druid2",
"roles": [
{
"name": "druidRole",
"permissions": [
{
"resourceAction": {
"resource": {
"name": "A",
"type": "DATASOURCE"
},
"action": "READ"
},
"resourceNamePattern": "A"
},
{
"resourceAction": {
"resource": {
"name": "C",
"type": "CONFIG"
},
"action": "WRITE"
},
"resourceNamePattern": "C"
}
]
}
]
}
```

The output format of this API when `?full` is specified is deprecated and in later versions will be switched to the output format used when both `?full` and `?simplifyPermissions` flag is set.

The `resourceNamePattern` is a compiled version of the resource name regex. It is redundant and complicates the use of this API for clients such as frontends that edit the authorization configuration, as the permission format in this output does not match the format used for adding permissions to a role.

- `?full?simplifyPermissions`: When both `?full` and `?simplifyPermissions` are set, the permissions in the output will contain only a list of `resourceAction` objects, without the extraneous `resourceNamePattern` field.

```json
{
"name": "druid2",
"roles": [
{
"name": "druidRole",
"users": null,
"permissions": [
{
"resource": {
"name": "A",
"type": "DATASOURCE"
},
"action": "READ"
},
{
"resource": {
"name": "C",
"type": "CONFIG"
},
"action": "WRITE"
}
]
}
]
}
```

`POST(/druid-ext/basic-security/authorization/db/{authorizerName}/users/{userName})`
Create a new user with name {userName}

Expand All @@ -184,7 +265,56 @@ Delete the user with name {userName}
Return a list of all role names.

`GET(/druid-ext/basic-security/authorization/db/{authorizerName}/roles/{roleName})`
Return name and permissions for the role named {roleName}
Return name and permissions for the role named {roleName}.

Example output:
```json
{
"name": "druidRole2",
"permissions": [
{
"resourceAction": {
"resource": {
"name": "E",
"type": "DATASOURCE"
},
"action": "WRITE"
},
"resourceNamePattern": "E"
}
]
}
```

The default output format of this API is deprecated and in later versions will be switched to the output format used when the `?simplifyPermissions` flag is set. The `resourceNamePattern` is a compiled version of the resource name regex. It is redundant and complicates the use of this API for clients such as frontends that edit the authorization configuration, as the permission format in this output does not match the format used for adding permissions to a role.

This API supports the following flags:

- `?full`: The output will contain an extra `users` list, containing the users that currently have this role.

```json
"users":["druid"]
```

- `?simplifyPermissions`: The permissions in the output will contain only a list of `resourceAction` objects, without the extraneous `resourceNamePattern` field. The `users` field will be null when `?full` is not specified.

Example output:
```json
{
"name": "druidRole2",
"users": null,
"permissions": [
{
"resource": {
"name": "E",
"type": "DATASOURCE"
},
"action": "WRITE"
}
]
}
```


`POST(/druid-ext/basic-security/authorization/db/{authorizerName}/roles/{roleName})`
Create a new role with name {roleName}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,11 @@ public Response getUser(
@Context HttpServletRequest req,
@PathParam("authorizerName") final String authorizerName,
@PathParam("userName") final String userName,
@QueryParam("full") String full
@QueryParam("full") String full,
@QueryParam("simplifyPermissions") String simplifyPermissions
)
{
return resourceHandler.getUser(authorizerName, userName, full != null);
return resourceHandler.getUser(authorizerName, userName, full != null, simplifyPermissions != null);
}

/**
Expand Down Expand Up @@ -208,10 +209,11 @@ public Response getRole(
@Context HttpServletRequest req,
@PathParam("authorizerName") final String authorizerName,
@PathParam("roleName") final String roleName,
@QueryParam("full") String full
@QueryParam("full") String full,
@QueryParam("simplifyPermissions") String simplifyPermissions
)
{
return resourceHandler.getRole(authorizerName, roleName, full != null);
return resourceHandler.getRole(authorizerName, roleName, full != null, simplifyPermissions != null);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ public interface BasicAuthorizerResourceHandler
// coordinator methods
Response getAllUsers(String authorizerName);

Response getUser(String authorizerName, String userName, boolean isFull);
Response getUser(String authorizerName, String userName, boolean isFull, boolean simplifyPermissions);

Response createUser(String authorizerName, String userName);

Response deleteUser(String authorizerName, String userName);

Response getAllRoles(String authorizerName);

Response getRole(String authorizerName, String roleName, boolean isFull);
Response getRole(String authorizerName, String roleName, boolean isFull, boolean simplifyPermissions);

Response createRole(String authorizerName, String roleName);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
import org.apache.druid.security.basic.authorization.db.updater.BasicAuthorizerMetadataStorageUpdater;
import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerRole;
import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerRoleFull;
import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerRoleSimplifiedPermissions;
import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerUser;
import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerUserFull;
import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerUserFullSimplifiedPermissions;
import org.apache.druid.security.basic.authorization.entity.UserAndRoleMap;
import org.apache.druid.server.security.Authorizer;
import org.apache.druid.server.security.AuthorizerMapper;
Expand Down Expand Up @@ -92,15 +94,15 @@ public Response getAllUsers(String authorizerName)
}

@Override
public Response getUser(String authorizerName, String userName, boolean isFull)
public Response getUser(String authorizerName, String userName, boolean isFull, boolean simplifyPermissions)
{
final BasicRoleBasedAuthorizer authorizer = authorizerMap.get(authorizerName);
if (authorizer == null) {
return makeResponseForAuthorizerNotFound(authorizerName);
}

if (isFull) {
return getUserFull(authorizerName, userName);
return getUserFull(authorizerName, userName, simplifyPermissions);
} else {
return getUserSimple(authorizerName, userName);
}
Expand Down Expand Up @@ -157,17 +159,17 @@ public Response getAllRoles(String authorizerName)
}

@Override
public Response getRole(String authorizerName, String roleName, boolean isFull)
public Response getRole(String authorizerName, String roleName, boolean isFull, boolean simplifyPermissions)
{
final BasicRoleBasedAuthorizer authorizer = authorizerMap.get(authorizerName);
if (authorizer == null) {
return makeResponseForAuthorizerNotFound(authorizerName);
}

if (isFull) {
return getRoleFull(authorizerName, roleName);
return getRoleFull(authorizerName, roleName, simplifyPermissions);
} else {
return getRoleSimple(authorizerName, roleName);
return getRoleSimple(authorizerName, roleName, simplifyPermissions);
}
}

Expand Down Expand Up @@ -335,7 +337,7 @@ private Response getUserSimple(String authorizerName, String userName)
}
}

private Response getUserFull(String authorizerName, String userName)
private Response getUserFull(String authorizerName, String userName, boolean simplifyPermissions)
{
Map<String, BasicAuthorizerUser> userMap = BasicAuthUtils.deserializeAuthorizerUserMap(
objectMapper,
Expand All @@ -353,25 +355,65 @@ private Response getUserFull(String authorizerName, String userName)
throw new BasicSecurityDBResourceException("User [%s] does not exist.", userName);
}

Set<BasicAuthorizerRole> roles = new HashSet<>();
for (String roleName : user.getRoles()) {
BasicAuthorizerRole role = roleMap.get(roleName);
if (role == null) {
log.error("User [%s] had role [%s], but role was not found.", userName, roleName);
} else {
roles.add(role);
}
if (simplifyPermissions) {
Set<BasicAuthorizerRoleSimplifiedPermissions> roles = getRolesForUserWithSimplifiedPermissions(user, roleMap);
BasicAuthorizerUserFullSimplifiedPermissions fullUser = new BasicAuthorizerUserFullSimplifiedPermissions(
userName,
roles
);
return Response.ok(fullUser).build();
} else {
Set<BasicAuthorizerRole> roles = getRolesForUser(user, roleMap);
BasicAuthorizerUserFull fullUser = new BasicAuthorizerUserFull(userName, roles);
return Response.ok(fullUser).build();
}

BasicAuthorizerUserFull fullUser = new BasicAuthorizerUserFull(userName, roles);
return Response.ok(fullUser).build();
}
catch (BasicSecurityDBResourceException e) {
return makeResponseForBasicSecurityDBResourceException(e);
}
}

private Response getRoleSimple(String authorizerName, String roleName)
private Set<BasicAuthorizerRoleSimplifiedPermissions> getRolesForUserWithSimplifiedPermissions(
BasicAuthorizerUser user,
Map<String, BasicAuthorizerRole> roleMap
)
{
Set<BasicAuthorizerRoleSimplifiedPermissions> roles = new HashSet<>();
for (String roleName : user.getRoles()) {
BasicAuthorizerRole role = roleMap.get(roleName);
if (role == null) {
log.error("User [%s] had role [%s], but role object was not found.", user.getName(), roleName);
} else {
BasicAuthorizerRoleSimplifiedPermissions roleWithSimplifiedPermissions = new BasicAuthorizerRoleSimplifiedPermissions(
role.getName(),
null,
BasicAuthorizerRoleSimplifiedPermissions.convertPermissions(role.getPermissions())
);
roles.add(roleWithSimplifiedPermissions);
}
}
return roles;
}

private Set<BasicAuthorizerRole> getRolesForUser(
BasicAuthorizerUser user,
Map<String, BasicAuthorizerRole> roleMap
)
{
Set<BasicAuthorizerRole> roles = new HashSet<>();
for (String roleName : user.getRoles()) {
BasicAuthorizerRole role = roleMap.get(roleName);
if (role == null) {
log.error("User [%s] had role [%s], but role object was not found.", user.getName(), roleName);
} else {
roles.add(role);
}
}
return roles;
}


private Response getRoleSimple(String authorizerName, String roleName, boolean simplifyPermissions)
{
Map<String, BasicAuthorizerRole> roleMap = BasicAuthUtils.deserializeAuthorizerRoleMap(
objectMapper,
Expand All @@ -383,14 +425,19 @@ private Response getRoleSimple(String authorizerName, String roleName)
if (role == null) {
throw new BasicSecurityDBResourceException("Role [%s] does not exist.", roleName);
}
return Response.ok(role).build();

if (simplifyPermissions) {
return Response.ok(new BasicAuthorizerRoleSimplifiedPermissions(role, null)).build();
} else {
return Response.ok(role).build();
}
}
catch (BasicSecurityDBResourceException e) {
return makeResponseForBasicSecurityDBResourceException(e);
}
}

private Response getRoleFull(String authorizerName, String roleName)
private Response getRoleFull(String authorizerName, String roleName, boolean simplifyPermissions)
{
Map<String, BasicAuthorizerRole> roleMap = BasicAuthUtils.deserializeAuthorizerRoleMap(
objectMapper,
Expand All @@ -414,12 +461,16 @@ private Response getRoleFull(String authorizerName, String roleName)
if (role == null) {
throw new BasicSecurityDBResourceException("Role [%s] does not exist.", roleName);
}
BasicAuthorizerRoleFull roleFull = new BasicAuthorizerRoleFull(
roleName,
users,
role.getPermissions()
);
return Response.ok(roleFull).build();
if (simplifyPermissions) {
return Response.ok(new BasicAuthorizerRoleSimplifiedPermissions(role, users)).build();
} else {
BasicAuthorizerRoleFull roleFull = new BasicAuthorizerRoleFull(
roleName,
users,
role.getPermissions()
);
return Response.ok(roleFull).build();
}
}
catch (BasicSecurityDBResourceException e) {
return makeResponseForBasicSecurityDBResourceException(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,14 @@ public DefaultBasicAuthorizerResourceHandler(
}
}


@Override
public Response getAllUsers(String authorizerName)
{
return NOT_FOUND_RESPONSE;
}

@Override
public Response getUser(String authorizerName, String userName, boolean isFull)
public Response getUser(String authorizerName, String userName, boolean isFull, boolean simplifyPermissions)
{
return NOT_FOUND_RESPONSE;
}
Expand All @@ -95,7 +94,7 @@ public Response getAllRoles(String authorizerName)
}

@Override
public Response getRole(String authorizerName, String roleName, boolean isFull)
public Response getRole(String authorizerName, String roleName, boolean isFull, boolean simplifyPermissions)
{
return NOT_FOUND_RESPONSE;
}
Expand Down
Loading