Fix #3056: Introduce team owner and let them manage their own team; Fix #3516: Authorizer should expect an owner field to be populated before granting permissions. (#3517)

This commit is contained in:
Sriharsha Chintalapani 2022-03-18 18:33:24 -07:00 committed by GitHub
parent a1941301af
commit 0b30c9f08d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 92 additions and 19 deletions

View File

@ -327,7 +327,11 @@ public class ChartResource {
Fields fields = new Fields(ALLOWED_FIELDS, FIELDS);
Chart chart = dao.get(uriInfo, id, fields);
SecurityUtil.checkAdminRoleOrPermissions(
authorizer, securityContext, dao.getEntityInterface(chart).getEntityReference(), patch);
authorizer,
securityContext,
dao.getEntityInterface(chart).getEntityReference(),
dao.getOriginalOwner(chart),
patch);
PatchResponse<Chart> response =
dao.patch(uriInfo, UUID.fromString(id), securityContext.getUserPrincipal().getName(), patch);

View File

@ -334,7 +334,11 @@ public class DashboardResource {
Fields fields = new Fields(ALLOWED_FIELDS, FIELDS);
Dashboard dashboard = dao.get(uriInfo, id, fields);
SecurityUtil.checkAdminRoleOrPermissions(
authorizer, securityContext, dao.getEntityInterface(dashboard).getEntityReference(), patch);
authorizer,
securityContext,
dao.getEntityInterface(dashboard).getEntityReference(),
dao.getOwnerReference(dashboard),
patch);
PatchResponse<Dashboard> response =
dao.patch(uriInfo, UUID.fromString(id), securityContext.getUserPrincipal().getName(), patch);

View File

@ -341,7 +341,11 @@ public class DatabaseResource {
Fields fields = new Fields(ALLOWED_FIELDS, FIELDS);
Database database = dao.get(uriInfo, id, fields);
SecurityUtil.checkAdminRoleOrPermissions(
authorizer, securityContext, dao.getEntityInterface(database).getEntityReference(), patch);
authorizer,
securityContext,
dao.getEntityInterface(database).getEntityReference(),
dao.getOriginalOwner(database),
patch);
PatchResponse<Database> response =
dao.patch(uriInfo, UUID.fromString(id), securityContext.getUserPrincipal().getName(), patch);

View File

@ -364,7 +364,11 @@ public class TableResource {
Fields fields = new Fields(ALLOWED_FIELDS, FIELDS);
Table table = dao.get(uriInfo, id, fields);
SecurityUtil.checkAdminRoleOrPermissions(
authorizer, securityContext, dao.getEntityInterface(table).getEntityReference(), patch);
authorizer,
securityContext,
dao.getEntityInterface(table).getEntityReference(),
dao.getOriginalOwner(table),
patch);
PatchResponse<Table> response =
dao.patch(uriInfo, UUID.fromString(id), securityContext.getUserPrincipal().getName(), patch);

View File

@ -331,7 +331,11 @@ public class GlossaryResource {
Fields fields = new Fields(ALLOWED_FIELDS, FIELDS);
Glossary glossary = dao.get(uriInfo, id, fields);
SecurityUtil.checkAdminRoleOrPermissions(
authorizer, securityContext, dao.getEntityInterface(glossary).getEntityReference(), patch);
authorizer,
securityContext,
dao.getEntityInterface(glossary).getEntityReference(),
dao.getOwnerReference(glossary),
patch);
PatchResponse<Glossary> response =
dao.patch(uriInfo, UUID.fromString(id), securityContext.getUserPrincipal().getName(), patch);
addHref(uriInfo, response.getEntity());

View File

@ -368,7 +368,11 @@ public class GlossaryTermResource {
Fields fields = new Fields(ALLOWED_FIELDS, FIELDS);
GlossaryTerm term = dao.get(uriInfo, id, fields);
SecurityUtil.checkAdminRoleOrPermissions(
authorizer, securityContext, dao.getEntityInterface(term).getEntityReference(), patch);
authorizer,
securityContext,
dao.getEntityInterface(term).getEntityReference(),
dao.getOriginalOwner(term),
patch);
PatchResponse<GlossaryTerm> response =
dao.patch(uriInfo, UUID.fromString(id), securityContext.getUserPrincipal().getName(), patch);
addHref(uriInfo, response.getEntity());

View File

@ -401,7 +401,11 @@ public class LocationResource {
Fields fields = new Fields(ALLOWED_FIELDS, FIELDS);
Location location = dao.get(uriInfo, id, fields);
SecurityUtil.checkAdminRoleOrPermissions(
authorizer, securityContext, dao.getEntityInterface(location).getEntityReference(), patch);
authorizer,
securityContext,
dao.getEntityInterface(location).getEntityReference(),
dao.getOwnerReference(location),
patch);
PatchResponse<Location> response =
dao.patch(uriInfo, UUID.fromString(id), securityContext.getUserPrincipal().getName(), patch);

View File

@ -272,7 +272,11 @@ public class MlModelResource {
Fields fields = new Fields(ALLOWED_FIELDS, FIELDS);
MlModel mlModel = dao.get(uriInfo, id, fields);
SecurityUtil.checkAdminRoleOrPermissions(
authorizer, securityContext, dao.getEntityInterface(mlModel).getEntityReference(), patch);
authorizer,
securityContext,
dao.getEntityInterface(mlModel).getEntityReference(),
dao.getOwnerReference(mlModel),
patch);
PatchResponse<MlModel> response =
dao.patch(uriInfo, UUID.fromString(id), securityContext.getUserPrincipal().getName(), patch);

View File

@ -370,7 +370,11 @@ public class AirflowPipelineResource {
Fields fields = new Fields(ALLOWED_FIELDS, FIELDS);
AirflowPipeline airflowPipeline = dao.get(uriInfo, id, fields);
SecurityUtil.checkAdminRoleOrPermissions(
authorizer, securityContext, dao.getEntityInterface(airflowPipeline).getEntityReference(), patch);
authorizer,
securityContext,
dao.getEntityInterface(airflowPipeline).getEntityReference(),
dao.getOriginalOwner(airflowPipeline),
patch);
PatchResponse<AirflowPipeline> response =
dao.patch(uriInfo, UUID.fromString(id), securityContext.getUserPrincipal().getName(), patch);

View File

@ -334,7 +334,11 @@ public class PipelineResource {
Fields fields = new Fields(ALLOWED_FIELDS, FIELDS);
Pipeline pipeline = dao.get(uriInfo, id, fields);
SecurityUtil.checkAdminRoleOrPermissions(
authorizer, securityContext, dao.getEntityInterface(pipeline).getEntityReference(), patch);
authorizer,
securityContext,
dao.getEntityInterface(pipeline).getEntityReference(),
dao.getOwnerReference(pipeline),
patch);
PatchResponse<Pipeline> response =
dao.patch(uriInfo, UUID.fromString(id), securityContext.getUserPrincipal().getName(), patch);

View File

@ -307,8 +307,8 @@ public class TeamResource {
public Response createOrUpdateTeam(
@Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid CreateTeam ct)
throws IOException, ParseException {
SecurityUtil.checkAdminOrBotRole(authorizer, securityContext);
Team team = getTeam(ct, securityContext);
SecurityUtil.checkAdminOrBotOrOwner(authorizer, securityContext, dao.getOriginalOwner(team));
RestUtil.PutResponse<Team> response = dao.createOrUpdate(uriInfo, team);
addHref(uriInfo, response.getEntity());
return response.toResponse();
@ -339,7 +339,11 @@ public class TeamResource {
Fields fields = new Fields(ALLOWED_FIELDS, FIELDS);
Team team = dao.get(uriInfo, id, fields);
SecurityUtil.checkAdminRoleOrPermissions(
authorizer, securityContext, dao.getEntityInterface(team).getEntityReference(), patch);
authorizer,
securityContext,
dao.getEntityInterface(team).getEntityReference(),
dao.getOriginalOwner(team),
patch);
PatchResponse<Team> response =
dao.patch(uriInfo, UUID.fromString(id), securityContext.getUserPrincipal().getName(), patch);
addHref(uriInfo, response.getEntity());
@ -370,6 +374,7 @@ public class TeamResource {
.withDescription(ct.getDescription())
.withDisplayName(ct.getDisplayName())
.withProfile(ct.getProfile())
.withOwner(ct.getOwner())
.withUpdatedBy(securityContext.getUserPrincipal().getName())
.withUpdatedAt(System.currentTimeMillis())
.withUsers(dao.getEntityReferences(ct.getUsers()))

View File

@ -332,7 +332,11 @@ public class TopicResource {
Fields fields = new Fields(ALLOWED_FIELDS, FIELD_OWNER);
Topic topic = dao.get(uriInfo, id, fields);
SecurityUtil.checkAdminRoleOrPermissions(
authorizer, securityContext, dao.getEntityInterface(topic).getEntityReference(), patch);
authorizer,
securityContext,
dao.getEntityInterface(topic).getEntityReference(),
dao.getOwnerReference(topic),
patch);
PatchResponse<Topic> response =
dao.patch(uriInfo, UUID.fromString(id), securityContext.getUserPrincipal().getName(), patch);

View File

@ -127,9 +127,12 @@ public class DefaultAuthorizer implements Authorizer {
@Override
public boolean hasPermissions(AuthenticationContext ctx, EntityReference owner) {
validateAuthenticationContext(ctx);
// To encourage users to claim or update changes to tables when a non-owner or un-claimed datasets.
// Since we have roles and operations. An Admin could enable updateDescription, tags, ownership permissions to
// a role and assign that to the users who can update the entities. With this we can look at the owner as a strict
// requirement to manage entities. So if owner is null we will not allow users to update entities. They can get a
// role that allows them to update the entity.
if (owner == null) {
return true;
return false;
}
try {
User user = getUserFromAuthenticationContext(ctx);

View File

@ -103,11 +103,17 @@ public final class SecurityUtil {
* metadata operations that can be derived from JSON patch.
*/
public static void checkAdminRoleOrPermissions(
Authorizer authorizer, SecurityContext securityContext, EntityReference entityReference, JsonPatch patch) {
Authorizer authorizer,
SecurityContext securityContext,
EntityReference entityReference,
EntityReference ownerReference,
JsonPatch patch) {
Principal principal = securityContext.getUserPrincipal();
AuthenticationContext authenticationCtx = SecurityUtil.getAuthenticationContext(principal);
if (authorizer.isAdmin(authenticationCtx) || authorizer.isBot(authenticationCtx)) {
if (authorizer.isAdmin(authenticationCtx)
|| authorizer.isBot(authenticationCtx)
|| authorizer.hasPermissions(authenticationCtx, ownerReference)) {
return;
}

View File

@ -29,12 +29,22 @@
"default": null
},
"defaultRoles": {
"description": "Roles to be assigned to all users that are part of this team.",
"description": "Roles to be assigned to all users that are part of this team.",
"type": "array",
"items": {
"$ref": "../../type/basic.json#/definitions/uuid"
},
"default": null
},
"owner": {
"description": "Owner of this team. ",
"$ref": "../../type/entityReference.json",
"default": null
},
"joinable": {
"description": "This field describes if the users can join a team without any permission. By default this is set to True.",
"type": "boolean",
"default": "true"
}
},
"required": ["name"],

View File

@ -56,6 +56,11 @@
"description": "List of entities owned by the team.",
"$ref": "../../type/entityReference.json#/definitions/entityReferenceList"
},
"owner": {
"description": "Owner of this team. ",
"$ref": "../../type/entityReference.json",
"default": null
},
"changeDescription": {
"description": "Change that lead to this version of the entity.",
"$ref": "../../type/entityHistory.json#/definitions/changeDescription"

View File

@ -7,5 +7,5 @@ Provides metadata version information.
from incremental import Version
__version__ = Version("metadata", 0, 9, 0, dev=0)
__version__ = Version("metadata", 0, 9, 0, dev=1)
__all__ = ["__version__"]