From 75ce9aca9ed4a10d673b7f598dfbf7b2aa0141d3 Mon Sep 17 00:00:00 2001 From: luk-kaminski Date: Fri, 19 Jan 2024 13:49:04 +0100 Subject: [PATCH] =?UTF-8?q?New=20permissions=20for=20profiles.=20Fixed=20p?= =?UTF-8?q?ermission=20checks=20in=20a=20few=20places=E2=80=A6=20(#17986)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * New permissions for profiles. Fixed permission checks in a few places. Migration to upgrade built-in role. * Improved role name in migration * More verbose naming of profiles permissions * Permission naming more verbose in constants as well --- .../indexer/FieldTypeManagementModule.java | 2 + ...ManagerRoleReceivesProfilePermissions.java | 51 +++++++++++++++++++ .../FieldTypeMappingsResource.java | 30 +++++------ .../IndexFieldTypeProfileResource.java | 7 +++ .../shared/security/RestPermissions.java | 12 +++++ 5 files changed, 87 insertions(+), 15 deletions(-) create mode 100644 graylog2-server/src/main/java/org/graylog2/migrations/V20240118130000_FieldTypeMappingsManagerRoleReceivesProfilePermissions.java diff --git a/graylog2-server/src/main/java/org/graylog2/indexer/FieldTypeManagementModule.java b/graylog2-server/src/main/java/org/graylog2/indexer/FieldTypeManagementModule.java index 6ac5f28bba53..3b566441a1e5 100644 --- a/graylog2-server/src/main/java/org/graylog2/indexer/FieldTypeManagementModule.java +++ b/graylog2-server/src/main/java/org/graylog2/indexer/FieldTypeManagementModule.java @@ -17,6 +17,7 @@ package org.graylog2.indexer; import org.graylog2.migrations.V20230905081400_CreateFieldTypeMappingsManagerRole; +import org.graylog2.migrations.V20240118130000_FieldTypeMappingsManagerRoleReceivesProfilePermissions; import org.graylog2.plugin.PluginModule; import org.graylog2.rest.resources.system.field_types.FieldTypeMappingsResource; @@ -25,5 +26,6 @@ public class FieldTypeManagementModule extends PluginModule { protected void configure() { addSystemRestResource(FieldTypeMappingsResource.class); addMigration(V20230905081400_CreateFieldTypeMappingsManagerRole.class); + addMigration(V20240118130000_FieldTypeMappingsManagerRoleReceivesProfilePermissions.class); } } diff --git a/graylog2-server/src/main/java/org/graylog2/migrations/V20240118130000_FieldTypeMappingsManagerRoleReceivesProfilePermissions.java b/graylog2-server/src/main/java/org/graylog2/migrations/V20240118130000_FieldTypeMappingsManagerRoleReceivesProfilePermissions.java new file mode 100644 index 000000000000..f5e493d7b000 --- /dev/null +++ b/graylog2-server/src/main/java/org/graylog2/migrations/V20240118130000_FieldTypeMappingsManagerRoleReceivesProfilePermissions.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 Graylog, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + */ +package org.graylog2.migrations; + +import jakarta.inject.Inject; +import org.graylog2.shared.security.RestPermissions; + +import java.time.ZonedDateTime; +import java.util.Set; + +public class V20240118130000_FieldTypeMappingsManagerRoleReceivesProfilePermissions extends Migration { + private final MigrationHelpers helpers; + + @Inject + public V20240118130000_FieldTypeMappingsManagerRoleReceivesProfilePermissions(final MigrationHelpers migrationHelpers) { + this.helpers = migrationHelpers; + } + + @Override + public ZonedDateTime createdAt() { + return ZonedDateTime.parse("2024-01-18T13:00:00Z"); + } + + @Override + public void upgrade() { + helpers.ensureBuiltinRole("Field Type Mappings Manager", + "Grants full control over custom field type mappings and field type profiles for all index sets (built-in)", + Set.of(RestPermissions.TYPE_MAPPINGS_CREATE, + RestPermissions.TYPE_MAPPINGS_DELETE, + RestPermissions.TYPE_MAPPINGS_EDIT, + RestPermissions.TYPE_MAPPINGS_READ, + RestPermissions.MAPPING_PROFILES_CREATE, + RestPermissions.MAPPING_PROFILES_DELETE, + RestPermissions.MAPPING_PROFILES_EDIT, + RestPermissions.MAPPING_PROFILES_READ)); + } +} diff --git a/graylog2-server/src/main/java/org/graylog2/rest/resources/system/field_types/FieldTypeMappingsResource.java b/graylog2-server/src/main/java/org/graylog2/rest/resources/system/field_types/FieldTypeMappingsResource.java index 201df1ae3794..15e4b2b64344 100644 --- a/graylog2-server/src/main/java/org/graylog2/rest/resources/system/field_types/FieldTypeMappingsResource.java +++ b/graylog2-server/src/main/java/org/graylog2/rest/resources/system/field_types/FieldTypeMappingsResource.java @@ -22,15 +22,6 @@ import io.swagger.annotations.ApiParam; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; -import org.apache.shiro.authz.annotation.RequiresAuthentication; -import org.graylog2.audit.jersey.AuditEvent; -import org.graylog2.indexer.fieldtypes.mapping.FieldTypeMappingsService; -import org.graylog2.indexer.indexset.CustomFieldMapping; -import org.graylog2.indexer.indexset.CustomFieldMappings; -import org.graylog2.rest.bulk.model.BulkOperationResponse; -import org.graylog2.shared.rest.resources.RestResource; -import org.graylog2.shared.security.RestPermissions; - import jakarta.inject.Inject; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; @@ -40,6 +31,15 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; +import org.apache.shiro.authz.annotation.RequiresAuthentication; +import org.graylog2.audit.jersey.AuditEvent; +import org.graylog2.indexer.fieldtypes.mapping.FieldTypeMappingsService; +import org.graylog2.indexer.indexset.CustomFieldMapping; +import org.graylog2.indexer.indexset.CustomFieldMappings; +import org.graylog2.rest.bulk.model.BulkOperationResponse; +import org.graylog2.shared.rest.resources.RestResource; +import org.graylog2.shared.security.RestPermissions; + import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -82,7 +82,7 @@ public Map getAllFieldTypes() { public Response changeFieldType(@ApiParam(name = "request") @Valid @NotNull(message = "Request body is mandatory") final FieldTypeChangeRequest request) { - checkPermissionsForCreation(request.indexSetsIds()); + checkPermissions(request.indexSetsIds(), RestPermissions.TYPE_MAPPINGS_CREATE); var customMapping = new CustomFieldMapping(request.fieldName(), request.type()); fieldTypeMappingsService.changeFieldType(customMapping, request.indexSetsIds(), request.rotateImmediately()); @@ -101,7 +101,7 @@ public Response changeFieldType(@ApiParam(name = "request") public Response setProfile(@ApiParam(name = "request") @Valid @NotNull(message = "Request body is mandatory") final FieldTypeProfileChangeRequest request) { - checkPermissionsForCreation(request.indexSetsIds()); + checkPermissions(request.indexSetsIds(), RestPermissions.INDEXSETS_EDIT); fieldTypeMappingsService.setProfile(request.indexSetsIds(), request.profileId(), request.rotateImmediately()); return Response.ok().build(); @@ -118,7 +118,7 @@ public Response setProfile(@ApiParam(name = "request") public Response removeProfileFromIndexSets(@ApiParam(name = "request") @Valid @NotNull(message = "Request body is mandatory") final FieldTypeProfileUnsetRequest request) { - checkPermissionsForCreation(request.indexSetsIds()); + checkPermissions(request.indexSetsIds(), RestPermissions.INDEXSETS_EDIT); fieldTypeMappingsService.removeProfileFromIndexSets(request.indexSetsIds(), request.rotateImmediately()); return Response.ok().build(); @@ -135,12 +135,12 @@ public Response removeProfileFromIndexSets(@ApiParam(name = "request") public Map removeCustomMapping(@ApiParam(name = "request") @Valid @NotNull(message = "Request body is mandatory") final CustomFieldMappingRemovalRequest request) { - checkPermissionsForCreation(request.indexSetsIds()); + checkPermissions(request.indexSetsIds(), RestPermissions.TYPE_MAPPINGS_DELETE); return fieldTypeMappingsService.removeCustomMappingForFields(request.fieldNames(), request.indexSetsIds(), request.rotateImmediately()); } - private void checkPermissionsForCreation(final Set indexSetsIds) { - indexSetsIds.forEach(indexSetId -> checkPermission(RestPermissions.TYPE_MAPPINGS_CREATE, indexSetId)); + private void checkPermissions(final Set indexSetsIds, final String permission) { + indexSetsIds.forEach(indexSetId -> checkPermission(permission, indexSetId)); } } diff --git a/graylog2-server/src/main/java/org/graylog2/rest/resources/system/indexer/IndexFieldTypeProfileResource.java b/graylog2-server/src/main/java/org/graylog2/rest/resources/system/indexer/IndexFieldTypeProfileResource.java index bb1e8decbbc9..9f1a6051f66c 100644 --- a/graylog2-server/src/main/java/org/graylog2/rest/resources/system/indexer/IndexFieldTypeProfileResource.java +++ b/graylog2-server/src/main/java/org/graylog2/rest/resources/system/indexer/IndexFieldTypeProfileResource.java @@ -43,6 +43,7 @@ import org.graylog2.indexer.indexset.profile.IndexFieldTypeProfileWithUsages; import org.graylog2.rest.models.tools.responses.PageListResponse; import org.graylog2.shared.rest.resources.RestResource; +import org.graylog2.shared.security.RestPermissions; import java.util.List; @@ -71,6 +72,7 @@ public IndexFieldTypeProfileResource(final IndexFieldTypeProfileService profileS @NoAuditEvent("No change to the DB") @ApiOperation(value = "Gets profile by id") public IndexFieldTypeProfileWithUsages retrieveById(@ApiParam(name = "profile_id") @PathParam("profile_id") String profileId) { + checkPermission(RestPermissions.MAPPING_PROFILES_READ, profileId); return profileService.getWithUsages(profileId) .orElseThrow(() -> new NotFoundException("No profile with id : " + profileId)); } @@ -91,6 +93,7 @@ public PageListResponse getPage(@ApiParam(name @DefaultValue(IndexFieldTypeProfile.NAME_FIELD_NAME) @QueryParam("sort") String sort, @ApiParam(name = "order", value = "The sort direction", allowableValues = "asc, desc") @DefaultValue("asc") @QueryParam("order") String order) { + checkPermission(RestPermissions.MAPPING_PROFILES_READ); return profileService.getPaginated(query, filters, page, perPage, sort, order); } @@ -100,6 +103,7 @@ public PageListResponse getPage(@ApiParam(name @NoAuditEvent("No change to the DB") @ApiOperation(value = "Gets list of all profiles (their ids and names only)") public List getAll() { + checkPermission(RestPermissions.MAPPING_PROFILES_READ); return profileService.getAll(); } @@ -108,6 +112,7 @@ public List getAll() { @AuditEvent(type = INDEX_FIELD_TYPE_PROFILE_CREATE) @ApiOperation(value = "Creates a new profile") public IndexFieldTypeProfile create(@ApiParam(name = "profileData") IndexFieldTypeProfileData profileData) { + checkPermission(RestPermissions.MAPPING_PROFILES_CREATE); return profileService.save(new IndexFieldTypeProfile(profileData)); } @@ -116,6 +121,7 @@ public IndexFieldTypeProfile create(@ApiParam(name = "profileData") IndexFieldTy @AuditEvent(type = INDEX_FIELD_TYPE_PROFILE_UPDATE) @ApiOperation(value = "Updates existing profile") public void update(@ApiParam(name = "profile") IndexFieldTypeProfile profile) { + checkPermission(RestPermissions.MAPPING_PROFILES_EDIT, profile.id()); final boolean updated = profileService.update(profile.id(), profile); if (!updated) { throw new NotFoundException("Profile does not exist : " + profile.id()); @@ -128,6 +134,7 @@ public void update(@ApiParam(name = "profile") IndexFieldTypeProfile profile) { @AuditEvent(type = INDEX_FIELD_TYPE_PROFILE_DELETE) @ApiOperation(value = "Removes a profile") public void delete(@ApiParam(name = "profile_id") @PathParam("profile_id") String profileId) { + checkPermission(RestPermissions.MAPPING_PROFILES_DELETE, profileId); profileService.delete(profileId); } diff --git a/graylog2-server/src/main/java/org/graylog2/shared/security/RestPermissions.java b/graylog2-server/src/main/java/org/graylog2/shared/security/RestPermissions.java index 2bd19fe05098..021b2c729663 100644 --- a/graylog2-server/src/main/java/org/graylog2/shared/security/RestPermissions.java +++ b/graylog2-server/src/main/java/org/graylog2/shared/security/RestPermissions.java @@ -150,6 +150,10 @@ public class RestPermissions implements PluginPermissions { public static final String TYPE_MAPPINGS_DELETE = "typemappings:delete"; public static final String TYPE_MAPPINGS_EDIT = "typemappings:edit"; public static final String TYPE_MAPPINGS_READ = "typemappings:read"; + public static final String MAPPING_PROFILES_CREATE = "mappingprofiles:create"; + public static final String MAPPING_PROFILES_DELETE = "mappingprofiles:delete"; + public static final String MAPPING_PROFILES_EDIT = "mappingprofiles:edit"; + public static final String MAPPING_PROFILES_READ = "mappingprofiles:read"; public static final String URL_WHITELIST_READ = "urlwhitelist:read"; public static final String URL_WHITELIST_WRITE = "urlwhitelist:write"; public static final String USERS_CREATE = "users:create"; @@ -302,6 +306,14 @@ public class RestPermissions implements PluginPermissions { .add(create(SEARCH_FILTERS_READ, "")) .add(create(SEARCH_FILTERS_EDIT, "")) .add(create(SEARCH_FILTERS_DELETE, "")) + .add(create(TYPE_MAPPINGS_CREATE, "")) + .add(create(TYPE_MAPPINGS_DELETE, "")) + .add(create(TYPE_MAPPINGS_EDIT, "")) + .add(create(TYPE_MAPPINGS_READ, "")) + .add(create(MAPPING_PROFILES_CREATE, "")) + .add(create(MAPPING_PROFILES_DELETE, "")) + .add(create(MAPPING_PROFILES_EDIT, "")) + .add(create(MAPPING_PROFILES_READ, "")) .build(); // Standard set of PERMISSIONS of readers.