Fixes 8371 Remove bot flag from authorizeAdmin method (#8372)

* Fixes 8371 Remove bot flag from authorizeAdmin method

* Remove authorizeAdminOrBot method

* Fix merge errors
This commit is contained in:
Suresh Srinivas 2022-10-26 20:36:17 -07:00 committed by GitHub
parent 6d404ccdc1
commit ed8c74e9b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 169 additions and 112 deletions

View File

@ -115,7 +115,7 @@ public class ReportDataResource {
public Response addReportData(
@Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid ReportData reportData)
throws IOException {
authorizer.authorizeAdmin(securityContext, true);
authorizer.authorizeAdmin(securityContext);
return dao.addReportData(reportData);
}
}

View File

@ -389,7 +389,7 @@ public class WebAnalyticEventResource extends EntityResource<WebAnalyticEvent, W
@Context SecurityContext securityContext,
@Valid WebAnalyticEventData webAnalyticEventData)
throws IOException {
authorizer.authorizeAdmin(securityContext, true);
authorizer.authorizeAdmin(securityContext);
return dao.addWebAnalyticEventData(uriInfo, webAnalyticEventData);
}

View File

@ -760,7 +760,8 @@ public class TableResource extends EntityResource<Table, TableRepository> {
@Parameter(description = "Id of the table", schema = @Schema(type = "UUID")) @PathParam("id") UUID id,
@Valid SQLQuery sqlQuery)
throws IOException {
authorizer.authorizeAdmin(securityContext, true);
OperationContext operationContext = new OperationContext(entityType, MetadataOperation.EDIT_ALL);
authorizer.authorize(securityContext, operationContext, getResourceContextById(id));
Table table = dao.addQuery(id, sqlQuery);
return addHref(uriInfo, table);
}
@ -784,7 +785,8 @@ public class TableResource extends EntityResource<Table, TableRepository> {
@Parameter(description = "Id of the table", schema = @Schema(type = "string")) @PathParam("id") UUID id,
@Valid DataModel dataModel)
throws IOException {
authorizer.authorizeAdmin(securityContext, true);
OperationContext operationContext = new OperationContext(entityType, MetadataOperation.EDIT_ALL);
authorizer.authorize(securityContext, operationContext, getResourceContextById(id));
Table table = dao.addDataModel(id, dataModel);
return addHref(uriInfo, table);
}

View File

@ -454,7 +454,9 @@ public class TestCaseResource extends EntityResource<TestCase, TestCaseRepositor
String fqn,
@Valid TestCaseResult testCaseResult)
throws IOException {
authorizer.authorizeAdmin(securityContext, true);
ResourceContextInterface resourceContext = TestCaseResourceContext.builder().name(fqn).build();
OperationContext operationContext = new OperationContext(Entity.TABLE, MetadataOperation.EDIT_TESTS);
authorizer.authorize(securityContext, operationContext, resourceContext);
return dao.addTestCaseResult(uriInfo, fqn, testCaseResult).toResponse();
}
@ -522,7 +524,9 @@ public class TestCaseResource extends EntityResource<TestCase, TestCaseRepositor
@PathParam("timestamp")
Long timestamp)
throws IOException {
authorizer.authorizeAdmin(securityContext, true);
ResourceContextInterface resourceContext = TestCaseResourceContext.builder().name(fqn).build();
OperationContext operationContext = new OperationContext(Entity.TABLE, MetadataOperation.EDIT_TESTS);
authorizer.authorize(securityContext, operationContext, resourceContext);
return dao.deleteTestCaseResult(fqn, timestamp).toResponse();
}

View File

@ -125,7 +125,7 @@ public class BuildSearchIndexResource {
@Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid CreateEventPublisherJob createRequest)
throws IOException {
// Only admins can issue a reindex request
authorizer.authorizeAdmin(securityContext, false);
authorizer.authorizeAdmin(securityContext);
User user =
userRepository.getByName(null, securityContext.getUserPrincipal().getName(), userRepository.getFields("id"));
if (createRequest.getRunMode() == RunMode.BATCH) {
@ -150,7 +150,7 @@ public class BuildSearchIndexResource {
@Context UriInfo uriInfo, @Context SecurityContext securityContext, @PathParam("runMode") String runMode)
throws IOException {
// Only admins can issue a reindex request
authorizer.authorizeAdmin(securityContext, false);
authorizer.authorizeAdmin(securityContext);
// Check if there is a running job for reindex for requested entity
String record;
if (runMode.equals(RunMode.BATCH.toString())) {

View File

@ -357,7 +357,7 @@ public class FeedResource {
// don't throw any exception
} else {
// Only admins or bots can close or resolve task other than the above-mentioned users
authorizer.authorizeAdmin(securityContext, true);
authorizer.authorizeAdmin(securityContext);
}
}
}

View File

@ -52,6 +52,7 @@ import org.openmetadata.schema.entity.data.PipelineStatus;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation;
import org.openmetadata.service.Entity;
import org.openmetadata.service.jdbi3.CollectionDAO;
import org.openmetadata.service.jdbi3.ListFilter;
@ -60,6 +61,7 @@ import org.openmetadata.service.resources.Collection;
import org.openmetadata.service.resources.EntityResource;
import org.openmetadata.service.resources.dqtests.TestCaseResource;
import org.openmetadata.service.security.Authorizer;
import org.openmetadata.service.security.policyevaluator.OperationContext;
import org.openmetadata.service.util.RestUtil;
import org.openmetadata.service.util.ResultList;
@ -362,7 +364,8 @@ public class PipelineResource extends EntityResource<Pipeline, PipelineRepositor
@Parameter(description = "Id of the pipeline", schema = @Schema(type = "string")) @PathParam("fqn") String fqn,
@Valid PipelineStatus pipelineStatus)
throws IOException {
authorizer.authorizeAdmin(securityContext, true);
OperationContext operationContext = new OperationContext(entityType, MetadataOperation.EDIT_STATUS);
authorizer.authorize(securityContext, operationContext, getResourceContextByName(fqn));
Pipeline pipeline = dao.addPipelineStatus(fqn, pipelineStatus);
return addHref(uriInfo, pipeline);
}
@ -427,7 +430,8 @@ public class PipelineResource extends EntityResource<Pipeline, PipelineRepositor
@PathParam("timestamp")
Long timestamp)
throws IOException {
authorizer.authorizeAdmin(securityContext, true);
OperationContext operationContext = new OperationContext(entityType, MetadataOperation.EDIT_STATUS);
authorizer.authorize(securityContext, operationContext, getResourceContextByName(fqn));
Pipeline pipeline = dao.deletePipelineStatus(fqn, timestamp);
return addHref(uriInfo, pipeline);
}

View File

@ -27,7 +27,6 @@ import org.openmetadata.service.jdbi3.ServiceEntityRepository;
import org.openmetadata.service.resources.EntityResource;
import org.openmetadata.service.secrets.SecretsManager;
import org.openmetadata.service.secrets.SecretsManagerFactory;
import org.openmetadata.service.security.AuthorizationException;
import org.openmetadata.service.security.Authorizer;
import org.openmetadata.service.util.JsonUtils;
import org.openmetadata.service.util.ResultList;
@ -50,10 +49,7 @@ public abstract class ServiceEntityResource<
}
protected T decryptOrNullify(SecurityContext securityContext, T service) {
SecretsManager secretsManager = SecretsManagerFactory.getSecretsManager();
try {
authorizer.authorizeAdmin(securityContext, secretsManager.isLocal());
} catch (AuthorizationException e) {
if (!authorizer.decryptSecret(securityContext)) {
return nullifyRequiredConnectionParameters(service);
}
service.getConnection().setConfig(retrieveServiceConnectionConfig(service));

View File

@ -58,16 +58,16 @@ import org.openmetadata.service.util.FilterUtil;
import org.openmetadata.service.util.JsonUtils;
import org.openmetadata.service.util.ResultList;
/**
* Resource for managing OpenMetadata settings that an admin can change. Example - using APIs here, the conversation
* thread notification can be changed to include only events that an organization uses.
*/
@Path("/v1/settings")
@Api(value = "Settings Collection", tags = "Settings collection")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Collection(name = "settings")
@Slf4j
/**
* Resource for managing OpenMetadata settings that an admin can change. Example - using APIs here, the conversation
* thread notification can be changed to include only events that an organization uses.
*/
public class SettingsResource {
private final SettingsRepository settingsRepository;
private final Authorizer authorizer;
@ -144,7 +144,7 @@ public class SettingsResource {
content = @Content(mediaType = "application/json", schema = @Schema(implementation = SettingsList.class)))
})
public ResultList<Settings> list(@Context UriInfo uriInfo, @Context SecurityContext securityContext) {
authorizer.authorizeAdmin(securityContext, false);
authorizer.authorizeAdmin(securityContext);
return settingsRepository.listAllConfigs();
}
@ -162,7 +162,7 @@ public class SettingsResource {
content = @Content(mediaType = "application/json", schema = @Schema(implementation = SettingsList.class)))
})
public List<EventFilter> getBootstrapFilters(@Context UriInfo uriInfo, @Context SecurityContext securityContext) {
authorizer.authorizeAdmin(securityContext, false);
authorizer.authorizeAdmin(securityContext);
return bootStrappedFilters;
}
@ -180,7 +180,7 @@ public class SettingsResource {
content = @Content(mediaType = "application/json", schema = @Schema(implementation = SettingsList.class)))
})
public Response resetFilters(@Context UriInfo uriInfo, @Context SecurityContext securityContext) {
authorizer.authorizeAdmin(securityContext, false);
authorizer.authorizeAdmin(securityContext);
Settings settings =
new Settings().withConfigType(ACTIVITY_FEED_FILTER_SETTING).withConfigValue(bootStrappedFilters);
return settingsRepository.createNewSetting(settings);
@ -203,7 +203,7 @@ public class SettingsResource {
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@PathParam("settingName") String settingName) {
authorizer.authorizeAdmin(securityContext, false);
authorizer.authorizeAdmin(securityContext);
return settingsRepository.getConfigWithKey(settingName);
}
@ -221,7 +221,7 @@ public class SettingsResource {
})
public Response createOrUpdateSetting(
@Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid Settings settingName) {
authorizer.authorizeAdmin(securityContext, false);
authorizer.authorizeAdmin(securityContext);
return settingsRepository.createOrUpdate(settingName);
}
@ -245,7 +245,6 @@ public class SettingsResource {
@PathParam("entityName")
String entityName,
@Valid List<Filters> newFilter) {
authorizer.authorizeAdmin(securityContext, false);
return settingsRepository.updateEntityFilter(entityName, newFilter);
}
@ -272,7 +271,7 @@ public class SettingsResource {
@ExampleObject("[" + "{op:remove, path:/a}," + "{op:add, path: /b, value: val}" + "]")
}))
JsonPatch patch) {
authorizer.authorizeAdmin(securityContext, false);
authorizer.authorizeAdmin(securityContext);
return settingsRepository.patchSetting(settingName, patch);
}
}

View File

@ -48,6 +48,7 @@ import org.openmetadata.schema.api.tags.CreateTag;
import org.openmetadata.schema.api.tags.CreateTagCategory;
import org.openmetadata.schema.entity.tags.Tag;
import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation;
import org.openmetadata.schema.type.TagCategory;
import org.openmetadata.service.Entity;
import org.openmetadata.service.OpenMetadataApplicationConfig;
@ -56,7 +57,10 @@ import org.openmetadata.service.jdbi3.ListFilter;
import org.openmetadata.service.jdbi3.TagCategoryRepository;
import org.openmetadata.service.jdbi3.TagRepository;
import org.openmetadata.service.resources.Collection;
import org.openmetadata.service.resources.EntityResource;
import org.openmetadata.service.security.Authorizer;
import org.openmetadata.service.security.policyevaluator.OperationContext;
import org.openmetadata.service.security.policyevaluator.ResourceContext;
import org.openmetadata.service.util.EntityUtil.Fields;
import org.openmetadata.service.util.FullyQualifiedName;
import org.openmetadata.service.util.RestUtil;
@ -279,7 +283,9 @@ public class TagResource {
public Response createCategory(
@Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid CreateTagCategory create)
throws IOException {
authorizer.authorizeAdmin(securityContext, true);
OperationContext operationContext = new OperationContext(Entity.TAG_CATEGORY, MetadataOperation.CREATE);
ResourceContext resourceContext = EntityResource.getResourceContext(Entity.TAG_CATEGORY, daoCategory).build();
authorizer.authorize(securityContext, operationContext, resourceContext);
TagCategory category = getTagCategory(securityContext, create);
category = addHref(uriInfo, daoCategory.create(uriInfo, category));
return Response.created(category.getHref()).entity(category).build();
@ -306,7 +312,9 @@ public class TagResource {
String category,
@Valid CreateTag create)
throws IOException {
authorizer.authorizeAdmin(securityContext, true);
OperationContext operationContext = new OperationContext(Entity.TAG, MetadataOperation.CREATE);
ResourceContext resourceContext = EntityResource.getResourceContext(Entity.TAG, dao).build();
authorizer.authorize(securityContext, operationContext, resourceContext);
Tag tag = getTag(securityContext, create, FullyQualifiedName.build(category));
URI categoryHref = RestUtil.getHref(uriInfo, TAG_COLLECTION_PATH, category);
tag = addHref(categoryHref, dao.create(uriInfo, tag));
@ -342,7 +350,9 @@ public class TagResource {
String primaryTag,
@Valid CreateTag create)
throws IOException {
authorizer.authorizeAdmin(securityContext, true);
OperationContext operationContext = new OperationContext(Entity.TAG, MetadataOperation.CREATE);
ResourceContext resourceContext = EntityResource.getResourceContext(Entity.TAG, dao).build();
authorizer.authorize(securityContext, operationContext, resourceContext);
Tag tag = getTag(securityContext, create, FullyQualifiedName.build(category, primaryTag));
URI categoryHref = RestUtil.getHref(uriInfo, TAG_COLLECTION_PATH, category);
URI parentHRef = RestUtil.getHref(categoryHref, primaryTag);
@ -364,7 +374,10 @@ public class TagResource {
String categoryName,
@Valid CreateTagCategory create)
throws IOException {
authorizer.authorizeAdmin(securityContext, true);
OperationContext operationContext = new OperationContext(Entity.TAG_CATEGORY, MetadataOperation.EDIT_ALL);
ResourceContext resourceContext =
EntityResource.getResourceContext(Entity.TAG_CATEGORY, daoCategory).name(categoryName).build();
authorizer.authorize(securityContext, operationContext, resourceContext);
TagCategory category = getTagCategory(securityContext, create);
// TODO clean this up
if (categoryName.equals(create.getName())) { // Not changing the name
@ -398,8 +411,12 @@ public class TagResource {
String primaryTag,
@Valid CreateTag create)
throws IOException {
authorizer.authorizeAdmin(securityContext, true);
Tag tag = getTag(securityContext, create, FullyQualifiedName.build(categoryName));
OperationContext operationContext = new OperationContext(Entity.TAG, MetadataOperation.EDIT_ALL);
ResourceContext resourceContext = EntityResource.getResourceContext(Entity.TAG, dao).name(categoryName).build();
authorizer.authorize(securityContext, operationContext, resourceContext);
URI categoryHref = RestUtil.getHref(uriInfo, TAG_COLLECTION_PATH, categoryName);
RestUtil.PutResponse<?> response;
if (primaryTag.equals(create.getName())) { // Not changing the name
@ -442,7 +459,7 @@ public class TagResource {
String secondaryTag,
@Valid CreateTag create)
throws IOException {
authorizer.authorizeAdmin(securityContext, false);
authorizer.authorizeAdmin(securityContext);
Tag tag = getTag(securityContext, create, FullyQualifiedName.build(categoryName, primaryTag));
URI categoryHref = RestUtil.getHref(uriInfo, TAG_COLLECTION_PATH, categoryName);
URI parentHRef = RestUtil.getHref(categoryHref, primaryTag);
@ -471,7 +488,10 @@ public class TagResource {
@Context SecurityContext securityContext,
@Parameter(description = "Tag category id", schema = @Schema(type = "UUID")) @PathParam("id") UUID id)
throws IOException {
authorizer.authorizeAdmin(securityContext, true);
OperationContext operationContext = new OperationContext(Entity.TAG_CATEGORY, MetadataOperation.EDIT_ALL);
ResourceContext resourceContext =
EntityResource.getResourceContext(Entity.TAG_CATEGORY, daoCategory).id(id).build();
authorizer.authorize(securityContext, operationContext, resourceContext);
TagCategory tagCategory = daoCategory.delete(uriInfo, id);
addHref(uriInfo, tagCategory);
return new RestUtil.DeleteResponse<>(tagCategory, RestUtil.ENTITY_DELETED).toResponse();
@ -490,7 +510,10 @@ public class TagResource {
@Parameter(description = "Tag id", schema = @Schema(type = "string")) @PathParam("category") String category,
@Parameter(description = "Tag id", schema = @Schema(type = "UUID")) @PathParam("id") UUID id)
throws IOException {
authorizer.authorizeAdmin(securityContext, true);
OperationContext operationContext = new OperationContext(Entity.TAG, MetadataOperation.EDIT_ALL);
ResourceContext resourceContext = EntityResource.getResourceContext(Entity.TAG, dao).id(id).build();
authorizer.authorize(securityContext, operationContext, resourceContext);
Tag tag = dao.delete(uriInfo, id);
URI categoryHref = RestUtil.getHref(uriInfo, TAG_COLLECTION_PATH, category);
addHref(categoryHref, tag);

View File

@ -286,7 +286,7 @@ public class UserResource extends EntityResource<User, UserRepository> {
description = "Generate a random pwd",
responses = {@ApiResponse(responseCode = "200", description = "Random pwd")})
public Response generateRandomPassword(@Context UriInfo uriInfo, @Context SecurityContext securityContext) {
authorizer.authorizeAdmin(securityContext, false);
authorizer.authorizeAdmin(securityContext);
return Response.status(OK).entity(PasswordUtil.generateRandomPassword()).build();
}
@ -485,7 +485,7 @@ public class UserResource extends EntityResource<User, UserRepository> {
@Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid CreateUser create) throws IOException {
User user = getUser(securityContext, create);
if (Boolean.TRUE.equals(create.getIsAdmin())) {
authorizer.authorizeAdmin(securityContext, true);
authorizer.authorizeAdmin(securityContext);
}
if (Boolean.TRUE.equals(create.getIsBot())) {
addAuthMechanismToBot(user, create, uriInfo);
@ -552,8 +552,8 @@ public class UserResource extends EntityResource<User, UserRepository> {
MetadataOperation operation = resourceContext.getEntity() == null ? CREATE : EDIT_ALL;
dao.prepare(user);
if (Boolean.TRUE.equals(create.getIsAdmin()) || Boolean.TRUE.equals(create.getIsBot())) {
authorizer.authorizeAdmin(securityContext, true);
if (Boolean.TRUE.equals(create.getIsAdmin())) {
authorizer.authorizeAdmin(securityContext);
} else if (!securityContext.getUserPrincipal().getName().equals(user.getName())) {
// doing authorization check outside of authorizer here. We are checking if the logged-in user same as the user
// we are trying to update. One option is to set users.owner as user, however that is not supported for User.
@ -590,17 +590,10 @@ public class UserResource extends EntityResource<User, UserRepository> {
@Valid GenerateTokenRequest generateTokenRequest)
throws IOException {
User user = dao.get(uriInfo, id, Fields.EMPTY_FIELDS);
authorizeGenerateJWT(user);
authorizer.authorizeAdmin(securityContext, false);
JWTAuthMechanism jwtAuthMechanism =
jwtTokenGenerator.generateJWTToken(user, generateTokenRequest.getJWTTokenExpiry());
AuthenticationMechanism authenticationMechanism =
new AuthenticationMechanism().withConfig(jwtAuthMechanism).withAuthType(AuthenticationMechanism.AuthType.JWT);
user.setAuthenticationMechanism(authenticationMechanism);
authorizer.authorizeAdmin(securityContext);
jwtTokenGenerator.setAuthMechanism(user, generateTokenRequest);
User updatedUser = dao.createOrUpdate(uriInfo, user).getEntity();
jwtAuthMechanism =
JsonUtils.convertValue(updatedUser.getAuthenticationMechanism().getConfig(), JWTAuthMechanism.class);
return Response.status(Response.Status.OK).entity(jwtAuthMechanism).build();
return Response.status(Response.Status.OK).entity(jwtTokenGenerator.getAuthMechanism(updatedUser)).build();
}
@PUT
@ -622,8 +615,7 @@ public class UserResource extends EntityResource<User, UserRepository> {
@Context UriInfo uriInfo, @Context SecurityContext securityContext, @PathParam("id") UUID id) throws IOException {
User user = dao.get(uriInfo, id, Fields.EMPTY_FIELDS);
authorizeGenerateJWT(user);
authorizer.authorizeAdmin(securityContext, false);
authorizer.authorizeAdmin(securityContext);
JWTAuthMechanism jwtAuthMechanism = new JWTAuthMechanism().withJWTToken(StringUtils.EMPTY);
AuthenticationMechanism authenticationMechanism =
new AuthenticationMechanism().withConfig(jwtAuthMechanism).withAuthType(JWT);
@ -658,7 +650,7 @@ public class UserResource extends EntityResource<User, UserRepository> {
throw new IllegalArgumentException("JWT token is only supported for bot users");
}
decryptOrNullify(securityContext, user);
authorizer.authorizeAdmin(securityContext, false);
authorizer.authorizeAdmin(securityContext);
AuthenticationMechanism authenticationMechanism = user.getAuthenticationMechanism();
if (authenticationMechanism != null
&& authenticationMechanism.getConfig() != null
@ -693,7 +685,7 @@ public class UserResource extends EntityResource<User, UserRepository> {
throw new IllegalArgumentException("JWT token is only supported for bot users");
}
decryptOrNullify(securityContext, user);
authorizer.authorizeAdmin(securityContext, false);
authorizer.authorizeAdmin(securityContext);
return user.getAuthenticationMechanism();
}
@ -724,8 +716,8 @@ public class UserResource extends EntityResource<User, UserRepository> {
JsonObject patchOpObject = patchOp.asJsonObject();
if (patchOpObject.containsKey("path") && patchOpObject.containsKey("value")) {
String path = patchOpObject.getString("path");
if (path.equals("/isAdmin") || path.equals("/isBot")) {
authorizer.authorizeAdmin(securityContext, true);
if (path.equals("/isAdmin")) {
authorizer.authorizeAdmin(securityContext);
}
// if path contains team, check if team is joinable by any user
if (patchOpObject.containsKey("op")
@ -742,7 +734,7 @@ public class UserResource extends EntityResource<User, UserRepository> {
dao.validateTeamAddition(id, UUID.fromString(teamId));
if (!dao.isTeamJoinable(teamId)) {
// Only admin can join closed teams
authorizer.authorizeAdmin(securityContext, false);
authorizer.authorizeAdmin(securityContext);
}
}
}
@ -1001,7 +993,7 @@ public class UserResource extends EntityResource<User, UserRepository> {
return Response.status(403).entity(new ErrorMessage(403, "Old Password is not correct")).build();
}
} else {
authorizer.authorizeAdmin(securityContext, false);
authorizer.authorizeAdmin(securityContext);
User storedUser = dao.getByName(uriInfo, request.getUsername(), new Fields(fields, String.join(",", fields)));
String newHashedPassword = BCrypt.withDefaults().hashToString(12, request.getNewPassword().toCharArray());
// Admin is allowed to set password for User directly
@ -1191,12 +1183,6 @@ public class UserResource extends EntityResource<User, UserRepository> {
.withRoles(EntityUtil.toEntityReferences(create.getRoles(), Entity.ROLE));
}
private void authorizeGenerateJWT(User user) {
if (!Boolean.TRUE.equals(user.getIsBot())) {
throw new IllegalArgumentException("Generating JWT token is only supported for bot users");
}
}
public User registerUser(UriInfo uriInfo, RegistrationRequest newRegistrationRequest) throws IOException {
String newRegistrationRequestEmail = newRegistrationRequest.getEmail();
String[] tokens = newRegistrationRequest.getEmail().split("@");

View File

@ -51,6 +51,7 @@ import org.openmetadata.schema.entity.data.Topic;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation;
import org.openmetadata.schema.type.topic.TopicSampleData;
import org.openmetadata.service.Entity;
import org.openmetadata.service.jdbi3.CollectionDAO;
@ -59,6 +60,7 @@ import org.openmetadata.service.jdbi3.TopicRepository;
import org.openmetadata.service.resources.Collection;
import org.openmetadata.service.resources.EntityResource;
import org.openmetadata.service.security.Authorizer;
import org.openmetadata.service.security.policyevaluator.OperationContext;
import org.openmetadata.service.util.ResultList;
@Path("/v1/topics")
@ -344,7 +346,8 @@ public class TopicResource extends EntityResource<Topic, TopicRepository> {
@Parameter(description = "Id of the topic", schema = @Schema(type = "string")) @PathParam("id") UUID id,
@Valid TopicSampleData sampleData)
throws IOException {
authorizer.authorizeAdmin(securityContext, true);
OperationContext operationContext = new OperationContext(entityType, MetadataOperation.EDIT_SAMPLE_DATA);
authorizer.authorize(securityContext, operationContext, getResourceContextById(id));
Topic topic = dao.addSampleData(id, sampleData);
return addHref(uriInfo, topic);
}

View File

@ -56,6 +56,7 @@ import org.openmetadata.schema.entity.type.Category;
import org.openmetadata.schema.entity.type.CustomProperty;
import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.MetadataOperation;
import org.openmetadata.service.Entity;
import org.openmetadata.service.OpenMetadataApplicationConfig;
import org.openmetadata.service.jdbi3.CollectionDAO;
@ -64,6 +65,7 @@ import org.openmetadata.service.jdbi3.TypeRepository;
import org.openmetadata.service.resources.Collection;
import org.openmetadata.service.resources.EntityResource;
import org.openmetadata.service.security.Authorizer;
import org.openmetadata.service.security.policyevaluator.OperationContext;
import org.openmetadata.service.util.EntityUtil.Fields;
import org.openmetadata.service.util.JsonUtils;
import org.openmetadata.service.util.RestUtil.PutResponse;
@ -389,7 +391,9 @@ public class TypeResource extends EntityResource<Type, TypeRepository> {
@Parameter(description = "Type Id", schema = @Schema(type = "string")) @PathParam("id") UUID id,
@Valid CustomProperty property)
throws IOException {
authorizer.authorizeAdmin(securityContext, false);
// TODO fix this is the typeID correct? Why are we not doing this by name?
OperationContext operationContext = new OperationContext(entityType, MetadataOperation.CREATE);
authorizer.authorize(securityContext, operationContext, getResourceContextById(id));
PutResponse<Type> response =
dao.addCustomProperty(uriInfo, securityContext.getUserPrincipal().getName(), id, property);
addHref(uriInfo, response.getEntity());

View File

@ -41,5 +41,7 @@ public interface Authorizer {
SecurityContext securityContext, OperationContext operationContext, ResourceContextInterface resourceContext)
throws IOException;
void authorizeAdmin(SecurityContext securityContext, boolean allowBots);
void authorizeAdmin(SecurityContext securityContext);
boolean decryptSecret(SecurityContext securityContext);
}

View File

@ -229,14 +229,28 @@ public class DefaultAuthorizer implements Authorizer {
}
@Override
public void authorizeAdmin(SecurityContext securityContext, boolean allowBots) {
public void authorizeAdmin(SecurityContext securityContext) {
SubjectContext subjectContext = getSubjectContext(securityContext);
if (subjectContext.isAdmin() || (allowBots && subjectContext.isBot())) {
if (subjectContext.isAdmin()) {
return;
}
throw new AuthorizationException(notAdmin(securityContext.getUserPrincipal().getName()));
}
@Override
public boolean decryptSecret(SecurityContext securityContext) {
SubjectContext subjectContext = getSubjectContext(securityContext);
if (subjectContext.isAdmin()) { // Always decrypt secrets for admin
return true;
}
SecretsManager secretsManager = SecretsManagerFactory.getSecretsManager();
if (subjectContext.isBot() && secretsManager.isLocal()) {
// Local secretsManager true means secrets are not encrypted. So allow decrypted secrets for bots.
return true;
}
return false;
}
private void addUsers(Set<String> users, String domain, Boolean isAdmin) {
for (String userName : users) {
User user = user(userName, domain, userName).withIsAdmin(isAdmin);

View File

@ -95,7 +95,12 @@ public class NoopAuthorizer implements Authorizer {
}
@Override
public void authorizeAdmin(SecurityContext securityContext, boolean allowBots) {
public void authorizeAdmin(SecurityContext securityContext) {
/* Always authorize */
}
@Override
public boolean decryptSecret(SecurityContext securityContext) {
return true; // Always decrypt
}
}

View File

@ -20,10 +20,13 @@ import java.util.List;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.openmetadata.schema.api.security.jwt.JWTTokenConfiguration;
import org.openmetadata.schema.entity.teams.AuthenticationMechanism;
import org.openmetadata.schema.entity.teams.User;
import org.openmetadata.schema.teams.authn.GenerateTokenRequest;
import org.openmetadata.schema.teams.authn.JWTAuthMechanism;
import org.openmetadata.schema.teams.authn.JWTTokenExpiry;
import org.openmetadata.service.security.AuthenticationException;
import org.openmetadata.service.util.JsonUtils;
@Slf4j
public class JWTTokenGenerator {
@ -65,6 +68,17 @@ public class JWTTokenGenerator {
}
}
public void setAuthMechanism(User user, GenerateTokenRequest generateTokenRequest) {
JWTAuthMechanism jwtAuthMechanism = generateJWTToken(user, generateTokenRequest.getJWTTokenExpiry());
AuthenticationMechanism authenticationMechanism =
new AuthenticationMechanism().withConfig(jwtAuthMechanism).withAuthType(AuthenticationMechanism.AuthType.JWT);
user.setAuthenticationMechanism(authenticationMechanism);
}
public JWTAuthMechanism getAuthMechanism(User user) {
return JsonUtils.convertValue(user.getAuthenticationMechanism().getConfig(), JWTAuthMechanism.class);
}
public JWTAuthMechanism generateJWTToken(User user, JWTTokenExpiry expiry) {
try {
JWTAuthMechanism jwtAuthMechanism = new JWTAuthMechanism().withJWTTokenExpiry(expiry);

View File

@ -39,6 +39,10 @@ public class OperationContext {
return operations;
}
public boolean isCreateOperation() {
return operations.contains(MetadataOperation.CREATE);
}
public static boolean isEditOperation(MetadataOperation operation) {
return operation.value().startsWith("Edit");
}

View File

@ -452,6 +452,17 @@
"EditDisplayName"
]
},
{
"name" : "type",
"operations" : [
"Create",
"Delete",
"ViewAll",
"EditAll",
"EditDescription",
"EditDisplayName"
]
},
{
"name" : "type",
"operations" : [

View File

@ -21,7 +21,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ -42,7 +41,6 @@ import org.openmetadata.service.jdbi3.CollectionDAO;
import org.openmetadata.service.jdbi3.ServiceEntityRepository;
import org.openmetadata.service.secrets.SecretsManager;
import org.openmetadata.service.secrets.SecretsManagerFactory;
import org.openmetadata.service.security.AuthorizationException;
import org.openmetadata.service.security.Authorizer;
@ExtendWith(MockitoExtension.class)
@ -105,21 +103,16 @@ public abstract class ServiceResourceTest<
throws IOException {
lenient().when(secretsManager.isLocal()).thenReturn(isLocalSecretManager);
if (isLocalSecretManager && !isAdmin && !isBot) {
lenient()
.doThrow(new AuthorizationException(""))
.when(authorizer)
.authorizeAdmin(any(SecurityContext.class), eq(true));
} else if (!isLocalSecretManager && !isAdmin) {
lenient()
.doThrow(new AuthorizationException(""))
.when(authorizer)
.authorizeAdmin(any(SecurityContext.class), eq(false));
if (isAdmin) {
lenient().doReturn(true).when(authorizer).decryptSecret(any(SecurityContext.class));
} else if (isLocalSecretManager && isBot) {
lenient().doReturn(true).when(authorizer).decryptSecret(any(SecurityContext.class));
} else {
lenient().doReturn(false).when(authorizer).decryptSecret(any(SecurityContext.class));
}
R actual = callGetFromResource(serviceResource);
verify(secretsManager, times(1)).isLocal();
verify(secretsManager)
.encryptOrDecryptServiceConnectionConfig(
notNull(), eq(serviceConnectionType()), any(), eq(serviceType()), eq(false));

View File

@ -82,6 +82,7 @@ import org.openmetadata.schema.auth.LoginRequest;
import org.openmetadata.schema.auth.RegistrationRequest;
import org.openmetadata.schema.entity.data.Table;
import org.openmetadata.schema.entity.teams.AuthenticationMechanism;
import org.openmetadata.schema.entity.teams.AuthenticationMechanism.AuthType;
import org.openmetadata.schema.entity.teams.Role;
import org.openmetadata.schema.entity.teams.Team;
import org.openmetadata.schema.entity.teams.User;
@ -90,6 +91,7 @@ import org.openmetadata.schema.teams.authn.GenerateTokenRequest;
import org.openmetadata.schema.teams.authn.JWTAuthMechanism;
import org.openmetadata.schema.teams.authn.JWTTokenExpiry;
import org.openmetadata.schema.teams.authn.SSOAuthMechanism;
import org.openmetadata.schema.teams.authn.SSOAuthMechanism.SsoServiceType;
import org.openmetadata.schema.type.ChangeDescription;
import org.openmetadata.schema.type.EntityReference;
import org.openmetadata.schema.type.ImageList;
@ -418,12 +420,7 @@ public class UserResourceTest extends EntityResourceTest<User, CreateUser> {
}
private CreateUser createBotUserRequest(TestInfo test, int index) {
return createRequest(test, index)
.withIsBot(true)
.withAuthenticationMechanism(
new AuthenticationMechanism()
.withAuthType(AuthenticationMechanism.AuthType.JWT)
.withConfig(new JWTAuthMechanism().withJWTTokenExpiry(JWTTokenExpiry.Unlimited)));
return createBotUserRequest(getEntityName(test, index));
}
@Test
@ -685,21 +682,18 @@ public class UserResourceTest extends EntityResourceTest<User, CreateUser> {
@Test
void put_generateToken_bot_user_200_ok(TestInfo test) throws HttpResponseException {
User user =
createEntity(
createRequest(test, 6)
.withName("ingestion-bot-jwt")
.withDisplayName("ingestion-bot-jwt")
.withEmail("ingestion-bot-jwt@email.com")
.withIsBot(true)
.withAuthenticationMechanism(
new AuthenticationMechanism()
.withAuthType(AuthenticationMechanism.AuthType.SSO)
.withConfig(
new SSOAuthMechanism()
.withSsoServiceType(SSOAuthMechanism.SsoServiceType.GOOGLE)
.withAuthConfig(new GoogleSSOClientConfig().withSecretKey("/path/to/secret.json")))),
authHeaders("ingestion-bot-jwt@email.com"));
AuthenticationMechanism authMechanism =
new AuthenticationMechanism()
.withAuthType(AuthType.SSO)
.withConfig(
new SSOAuthMechanism()
.withSsoServiceType(SsoServiceType.GOOGLE)
.withAuthConfig(new GoogleSSOClientConfig().withSecretKey("/path/to/secret.json")));
CreateUser create =
createBotUserRequest("ingestion-bot-jwt")
.withEmail("ingestion-bot-jwt@email.com")
.withAuthenticationMechanism(authMechanism);
User user = createEntity(create, authHeaders("ingestion-bot-jwt@email.com"));
TestUtils.put(
getResource(String.format("users/generateToken/%s", user.getId())),
new GenerateTokenRequest().withJWTTokenExpiry(JWTTokenExpiry.Seven),
@ -865,14 +859,14 @@ public class UserResourceTest extends EntityResourceTest<User, CreateUser> {
BotResourceTest botResourceTest = new BotResourceTest();
String botName = "test-bot-user-fail";
// create bot user
CreateUser createBotUser = creatBotUserRequest("test-bot-user", true).withBotName(botName);
CreateUser createBotUser = createBotUserRequest("test-bot-user").withBotName(botName);
User botUser = updateEntity(createBotUser, CREATED, ADMIN_AUTH_HEADERS);
EntityReference botUserRef = Objects.requireNonNull(botUser).getEntityReference();
// assign bot user to a bot
CreateBot create = botResourceTest.createRequest(test).withBotUser(botUserRef).withName(botName);
botResourceTest.createEntity(create, ADMIN_AUTH_HEADERS);
// put user with a different bot name
CreateUser createWrongBotUser = creatBotUserRequest("test-bot-user", true).withBotName("test-bot-user-fail-2");
CreateUser createWrongBotUser = createBotUserRequest("test-bot-user").withBotName("test-bot-user-fail-2");
assertResponse(
() -> updateEntity(createWrongBotUser, BAD_REQUEST, ADMIN_AUTH_HEADERS),
BAD_REQUEST,
@ -884,14 +878,14 @@ public class UserResourceTest extends EntityResourceTest<User, CreateUser> {
BotResourceTest botResourceTest = new BotResourceTest();
String botName = "test-bot-ok";
// create bot user
CreateUser createBotUser = creatBotUserRequest("test-bot-user-ok", true).withBotName(botName);
CreateUser createBotUser = createBotUserRequest("test-bot-user-ok").withBotName(botName);
User botUser = updateEntity(createBotUser, CREATED, ADMIN_AUTH_HEADERS);
EntityReference botUserRef = Objects.requireNonNull(botUser).getEntityReference();
// assign bot user to a bot
CreateBot create = botResourceTest.createRequest(test).withBotUser(botUserRef).withName(botName);
botResourceTest.createEntity(create, ADMIN_AUTH_HEADERS);
// put again user with same bot name
CreateUser createDifferentBotUser = creatBotUserRequest("test-bot-user-ok", true).withBotName(botName);
CreateUser createDifferentBotUser = createBotUserRequest("test-bot-user-ok").withBotName(botName);
updateEntity(createDifferentBotUser, OK, ADMIN_AUTH_HEADERS);
assertNotNull(createDifferentBotUser);
}
@ -1044,19 +1038,18 @@ public class UserResourceTest extends EntityResourceTest<User, CreateUser> {
return String.join(",", allowedFields);
}
public User createUser(String botName, boolean isBot) {
public User createUser(String userName, boolean isBot) {
try {
CreateUser createUser = creatBotUserRequest(botName, isBot);
CreateUser createUser = createBotUserRequest(userName).withIsBot(isBot);
return createEntity(createUser, ADMIN_AUTH_HEADERS);
} catch (Exception ignore) {
return null;
}
}
private CreateUser creatBotUserRequest(String botUserName, boolean isBot) {
private CreateUser createBotUserRequest(String botUserName) {
return createRequest(botUserName, "", "", null)
.withIsBot(isBot)
.withIsAdmin(false)
.withIsBot(true)
.withAuthenticationMechanism(
new AuthenticationMechanism()
.withAuthType(AuthenticationMechanism.AuthType.JWT)