From 023abfda1d255da487dd601dcbde7a1069f2862a Mon Sep 17 00:00:00 2001 From: Ajith Prasad <37380177+aji-aju@users.noreply.github.com> Date: Fri, 13 Jun 2025 13:12:41 +0530 Subject: [PATCH] Scim Interface and resource in OM (#21512) * SCIM interface * removed unwanted code * remove SCIM registration in OM * reverted scim configuration settings, as it is not needed * Added security context to create user * Added scimusername in user jsons * added externalid, scimUsername in createUser * Added security context on create and update groups * Added jakarta imports * Authorization added * Added role, policy and bot for SCIM --- .../OpenMetadataApplicationConfig.java | 4 + .../service/resources/scim/ScimResource.java | 358 ++++++++++++++++++ .../service/scim/ScimProvisioningService.java | 42 ++ .../impl/DefaultScimProvisioningService.java | 91 +++++ .../service/security/SecurityUtil.java | 14 +- .../openmetadata/service/util/UserUtil.java | 4 +- .../main/resources/json/data/bot/scimBot.json | 11 + .../resources/json/data/botUser/scimBot.json | 9 + .../json/data/policy/ScimBotPolicy.json | 18 + .../resources/json/data/role/ScimBotRole.json | 13 + .../json/schema/api/scim/scimGroup.json | 78 ++++ .../json/schema/api/scim/scimPatchOp.json | 39 ++ .../json/schema/api/scim/scimUser.json | 92 +++++ .../json/schema/api/teams/createTeam.json | 4 + .../json/schema/api/teams/createUser.json | 8 + .../accessControl/resourceDescriptor.json | 6 +- .../json/schema/entity/teams/team.json | 4 + .../json/schema/entity/teams/user.json | 8 + .../json/schema/scim/scimConfiguration.json | 23 ++ .../json/schema/settings/settings.json | 3 +- .../ui/src/generated/api/scim/scimGroup.ts | 73 ++++ .../ui/src/generated/api/scim/scimPatchOp.ts | 32 ++ .../ui/src/generated/api/scim/scimUser.ts | 84 ++++ .../ui/src/generated/api/teams/createTeam.ts | 5 + .../ui/src/generated/api/teams/createUser.ts | 8 + .../accessControl/resourceDescriptor.ts | 4 + .../ui/src/generated/entity/teams/team.ts | 5 + .../ui/src/generated/entity/teams/user.ts | 8 + .../src/generated/scim/scimConfiguration.ts | 26 ++ .../ui/src/generated/settings/settings.ts | 1 + 30 files changed, 1069 insertions(+), 6 deletions(-) create mode 100644 openmetadata-service/src/main/java/org/openmetadata/service/resources/scim/ScimResource.java create mode 100644 openmetadata-service/src/main/java/org/openmetadata/service/scim/ScimProvisioningService.java create mode 100644 openmetadata-service/src/main/java/org/openmetadata/service/scim/impl/DefaultScimProvisioningService.java create mode 100644 openmetadata-service/src/main/resources/json/data/bot/scimBot.json create mode 100644 openmetadata-service/src/main/resources/json/data/botUser/scimBot.json create mode 100644 openmetadata-service/src/main/resources/json/data/policy/ScimBotPolicy.json create mode 100644 openmetadata-service/src/main/resources/json/data/role/ScimBotRole.json create mode 100644 openmetadata-spec/src/main/resources/json/schema/api/scim/scimGroup.json create mode 100644 openmetadata-spec/src/main/resources/json/schema/api/scim/scimPatchOp.json create mode 100644 openmetadata-spec/src/main/resources/json/schema/api/scim/scimUser.json create mode 100644 openmetadata-spec/src/main/resources/json/schema/scim/scimConfiguration.json create mode 100644 openmetadata-ui/src/main/resources/ui/src/generated/api/scim/scimGroup.ts create mode 100644 openmetadata-ui/src/main/resources/ui/src/generated/api/scim/scimPatchOp.ts create mode 100644 openmetadata-ui/src/main/resources/ui/src/generated/api/scim/scimUser.ts create mode 100644 openmetadata-ui/src/main/resources/ui/src/generated/scim/scimConfiguration.ts diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/OpenMetadataApplicationConfig.java b/openmetadata-service/src/main/java/org/openmetadata/service/OpenMetadataApplicationConfig.java index 8cc60594961..53e1c833280 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/OpenMetadataApplicationConfig.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/OpenMetadataApplicationConfig.java @@ -32,6 +32,7 @@ import org.openmetadata.schema.api.security.AuthorizerConfiguration; import org.openmetadata.schema.api.security.OpsConfig; import org.openmetadata.schema.api.security.jwt.JWTTokenConfiguration; import org.openmetadata.schema.configuration.LimitsConfiguration; +import org.openmetadata.schema.security.scim.ScimConfiguration; import org.openmetadata.schema.security.secrets.SecretsManagerConfiguration; import org.openmetadata.schema.service.configuration.elasticsearch.ElasticSearchConfiguration; import org.openmetadata.service.config.OMWebConfiguration; @@ -142,6 +143,9 @@ public class OpenMetadataApplicationConfig extends Configuration { @Valid private ObjectStorageConfiguration objectStorage; + @JsonProperty("scimConfiguration") + private ScimConfiguration scimConfiguration; + @Override public String toString() { return "catalogConfig{" diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/scim/ScimResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/scim/ScimResource.java new file mode 100644 index 00000000000..4504a8ddde6 --- /dev/null +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/scim/ScimResource.java @@ -0,0 +1,358 @@ +package org.openmetadata.service.resources.scim; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.PATCH; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.SecurityContext; +import jakarta.ws.rs.core.UriInfo; +import java.util.List; +import org.openmetadata.schema.EntityInterface; +import org.openmetadata.schema.api.scim.ScimGroup; +import org.openmetadata.schema.api.scim.ScimPatchOp; +import org.openmetadata.schema.api.scim.ScimUser; +import org.openmetadata.schema.type.EntityReference; +import org.openmetadata.schema.type.MetadataOperation; +import org.openmetadata.schema.type.TagLabel; +import org.openmetadata.service.scim.ScimProvisioningService; +import org.openmetadata.service.security.Authorizer; +import org.openmetadata.service.security.policyevaluator.OperationContext; +import org.openmetadata.service.security.policyevaluator.ResourceContextInterface; + +@Path("/v1/scim") +@Tag(name = "SCIM", description = "SCIM 2.0 compliant user and group provisioning endpoints.") +@Produces({"application/json", "application/scim+json"}) +@Consumes({"application/json", "application/scim+json"}) +public class ScimResource { + + private static final String SCIM_RESOURCE_NAME = "scim"; + private final ScimProvisioningService provisioningService; + protected final Authorizer authorizer; + + public ScimResource(ScimProvisioningService provisioningService, Authorizer authorizer) { + this.provisioningService = provisioningService; + this.authorizer = authorizer; + } + + @GET + @Path("/") + @Operation( + operationId = "getServiceProviderConfig", + summary = "Get SCIM Service Provider Config", + description = "Returns the SCIM service provider configuration.", + responses = + @ApiResponse(responseCode = "200", description = "SCIM Service Provider Configuration")) + public Response getServiceProviderConfig(@Context SecurityContext securityContext) { + authorizer.authorize( + securityContext, + new OperationContext(SCIM_RESOURCE_NAME, MetadataOperation.VIEW_SCIM), + new ScimResourceContext()); + return provisioningService.getServiceProviderConfig(); + } + + @GET + @Path("/ServiceProviderConfig") + @Operation( + operationId = "getServiceProviderConfigAlias", + summary = "Alias endpoint for SCIM Service Provider Config", + description = "Alias endpoint for service provider configuration.", + responses = + @ApiResponse(responseCode = "200", description = "SCIM Service Provider Configuration")) + public Response getServiceProviderConfigAlias(@Context SecurityContext securityContext) { + return getServiceProviderConfig(securityContext); + } + + @GET + @Path("/Users") + @Operation( + operationId = "listScimUsers", + summary = "List SCIM users", + description = "Lists SCIM users based on optional filters.", + responses = @ApiResponse(responseCode = "200", description = "List of SCIM Users")) + public Response listUsers(@Context UriInfo uriInfo, @Context SecurityContext securityContext) { + authorizer.authorize( + securityContext, + new OperationContext(SCIM_RESOURCE_NAME, MetadataOperation.VIEW_SCIM), + new ScimResourceContext()); + return provisioningService.listUsers(uriInfo); + } + + @POST + @Path("/Users") + @Operation( + operationId = "createScimUser", + summary = "Create SCIM user", + description = "Creates a new SCIM user.", + responses = { + @ApiResponse(responseCode = "201", description = "User created"), + @ApiResponse(responseCode = "400", description = "Invalid user input") + }) + public Response createUser( + ScimUser user, @Context UriInfo uriInfo, @Context SecurityContext securityContext) { + authorizer.authorize( + securityContext, + new OperationContext(SCIM_RESOURCE_NAME, MetadataOperation.CREATE_SCIM), + new ScimResourceContext()); + return provisioningService.createUser(user, uriInfo, securityContext); + } + + @PUT + @Path("/Users/{id}") + @Operation( + operationId = "updateScimUser", + summary = "Update SCIM user", + description = "Updates a SCIM user identified by ID.", + responses = { + @ApiResponse(responseCode = "200", description = "User updated"), + @ApiResponse(responseCode = "404", description = "User not found") + }) + public Response updateUser( + @Parameter(description = "SCIM User ID") @PathParam("id") String id, + ScimUser user, + @Context UriInfo uriInfo, + @Context SecurityContext securityContext) { + authorizer.authorize( + securityContext, + new OperationContext(SCIM_RESOURCE_NAME, MetadataOperation.EDIT_SCIM), + new ScimResourceContext()); + return provisioningService.updateUser(id, user, uriInfo); + } + + @DELETE + @Path("/Users/{id}") + @Operation( + operationId = "deleteScimUser", + summary = "Delete SCIM user", + description = "Deletes a SCIM user identified by ID.", + responses = { + @ApiResponse(responseCode = "200", description = "User deleted"), + @ApiResponse(responseCode = "404", description = "User not found") + }) + public Response deleteUser( + @Parameter(description = "SCIM User ID") @PathParam("id") String id, + @Context UriInfo uriInfo, + @Context SecurityContext securityContext) { + authorizer.authorize( + securityContext, + new OperationContext(SCIM_RESOURCE_NAME, MetadataOperation.DELETE_SCIM), + new ScimResourceContext()); + return provisioningService.deleteUser(id, uriInfo, securityContext); + } + + @PATCH + @Path("/Users/{id}") + @Operation( + operationId = "patchScimUser", + summary = "Patch SCIM user", + description = "Patch updates to a SCIM user identified by ID.", + responses = { + @ApiResponse(responseCode = "200", description = "User patched"), + @ApiResponse(responseCode = "404", description = "User not found") + }) + public Response patchUser( + @Parameter(description = "SCIM User ID") @PathParam("id") String id, + ScimPatchOp request, + @Context UriInfo uriInfo, + @Context SecurityContext securityContext) { + authorizer.authorize( + securityContext, + new OperationContext(SCIM_RESOURCE_NAME, MetadataOperation.EDIT_SCIM), + new ScimResourceContext()); + return provisioningService.patchUser(id, request, uriInfo, securityContext); + } + + @GET + @Path("/Users/{id}") + @Operation( + operationId = "getScimUser", + summary = "Get SCIM user by ID", + description = "Retrieves a SCIM user identified by ID.", + responses = { + @ApiResponse(responseCode = "200", description = "User found"), + @ApiResponse(responseCode = "404", description = "User not found") + }) + public Response getUser( + @Parameter(description = "SCIM User ID") @PathParam("id") String id, + @Context UriInfo uriInfo, + @Context SecurityContext securityContext) { + authorizer.authorize( + securityContext, + new OperationContext(SCIM_RESOURCE_NAME, MetadataOperation.VIEW_SCIM), + new ScimResourceContext()); + return provisioningService.getUser(id, uriInfo); + } + + @GET + @Path("/Groups") + @Operation( + operationId = "listScimGroups", + summary = "List SCIM groups", + description = "Lists SCIM groups based on optional filters.", + responses = @ApiResponse(responseCode = "200", description = "List of SCIM Groups")) + public Response listGroups(@Context UriInfo uriInfo, @Context SecurityContext securityContext) { + authorizer.authorize( + securityContext, + new OperationContext(SCIM_RESOURCE_NAME, MetadataOperation.VIEW_SCIM), + new ScimResourceContext()); + return provisioningService.listGroups(uriInfo); + } + + @POST + @Path("/Groups") + @Operation( + operationId = "createScimGroup", + summary = "Create SCIM group", + description = "Creates a new SCIM group.", + responses = { + @ApiResponse(responseCode = "201", description = "Group created"), + @ApiResponse(responseCode = "400", description = "Invalid group input") + }) + public Response createGroup( + ScimGroup group, @Context UriInfo uriInfo, @Context SecurityContext securityContext) { + authorizer.authorize( + securityContext, + new OperationContext(SCIM_RESOURCE_NAME, MetadataOperation.CREATE_SCIM), + new ScimResourceContext()); + return provisioningService.createGroup(group, uriInfo, securityContext); + } + + @PUT + @Path("/Groups/{id}") + @Operation( + operationId = "updateScimGroup", + summary = "Update SCIM group", + description = "Updates a SCIM group identified by ID.", + responses = { + @ApiResponse(responseCode = "200", description = "Group updated"), + @ApiResponse(responseCode = "404", description = "Group not found") + }) + public Response updateGroup( + @Parameter(description = "SCIM Group ID") @PathParam("id") String id, + ScimGroup group, + @Context UriInfo uriInfo, + @Context SecurityContext securityContext) { + authorizer.authorize( + securityContext, + new OperationContext(SCIM_RESOURCE_NAME, MetadataOperation.EDIT_SCIM), + new ScimResourceContext()); + return provisioningService.updateGroup(id, group, uriInfo, securityContext); + } + + @GET + @Path("/Groups/{id}") + @Operation( + operationId = "getScimGroup", + summary = "Get SCIM group by ID", + description = "Retrieves a SCIM group identified by ID.", + responses = { + @ApiResponse(responseCode = "200", description = "Group found"), + @ApiResponse(responseCode = "404", description = "Group not found") + }) + public Response getGroup( + @Parameter(description = "SCIM Group ID") @PathParam("id") String id, + @Context UriInfo uriInfo, + @Context SecurityContext securityContext) { + authorizer.authorize( + securityContext, + new OperationContext(SCIM_RESOURCE_NAME, MetadataOperation.VIEW_SCIM), + new ScimResourceContext()); + return provisioningService.getGroup(id, uriInfo); + } + + @PATCH + @Path("/Groups/{id}") + @Operation( + operationId = "patchScimGroup", + summary = "Patch SCIM group", + description = "Patch updates to a SCIM group identified by ID.", + responses = { + @ApiResponse(responseCode = "200", description = "Group patched"), + @ApiResponse(responseCode = "404", description = "Group not found") + }) + public Response patchGroup( + @Parameter(description = "SCIM Group ID") @PathParam("id") String id, + ScimPatchOp request, + @Context UriInfo uriInfo, + @Context SecurityContext securityContext) { + authorizer.authorize( + securityContext, + new OperationContext(SCIM_RESOURCE_NAME, MetadataOperation.EDIT_SCIM), + new ScimResourceContext()); + return provisioningService.patchGroup(id, request, uriInfo, securityContext); + } + + @DELETE + @Path("/Groups/{id}") + @Operation( + operationId = "deleteScimGroup", + summary = "Delete SCIM group", + description = "Deletes a SCIM group identified by ID.", + responses = { + @ApiResponse(responseCode = "200", description = "Group deleted"), + @ApiResponse(responseCode = "404", description = "Group not found") + }) + public Response deleteGroup( + @Parameter(description = "SCIM Group ID") @PathParam("id") String id, + @Context UriInfo uriInfo, + @Context SecurityContext securityContext) { + authorizer.authorize( + securityContext, + new OperationContext(SCIM_RESOURCE_NAME, MetadataOperation.DELETE_SCIM), + new ScimResourceContext()); + return provisioningService.deleteGroup(id, uriInfo, securityContext); + } + + @GET + @Path("/Schemas") + @Operation( + operationId = "getScimSchemas", + summary = "Get SCIM schemas", + description = "Returns supported SCIM schemas.", + responses = @ApiResponse(responseCode = "200", description = "SCIM schemas")) + public Response getSchemas(@Context SecurityContext securityContext) { + authorizer.authorize( + securityContext, + new OperationContext(SCIM_RESOURCE_NAME, MetadataOperation.VIEW_SCIM), + new ScimResourceContext()); + return provisioningService.getSchemas(); + } + + static class ScimResourceContext implements ResourceContextInterface { + + @Override + public String getResource() { + return SCIM_RESOURCE_NAME; + } + + @Override + public List getOwners() { + return null; + } + + @Override + public List getTags() { + return null; + } + + @Override + public EntityInterface getEntity() { + return null; + } + + @Override + public EntityReference getDomain() { + return null; + } + } +} diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/scim/ScimProvisioningService.java b/openmetadata-service/src/main/java/org/openmetadata/service/scim/ScimProvisioningService.java new file mode 100644 index 00000000000..0c1dbec2064 --- /dev/null +++ b/openmetadata-service/src/main/java/org/openmetadata/service/scim/ScimProvisioningService.java @@ -0,0 +1,42 @@ +package org.openmetadata.service.scim; + +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.SecurityContext; +import jakarta.ws.rs.core.UriInfo; +import org.openmetadata.schema.api.scim.ScimGroup; +import org.openmetadata.schema.api.scim.ScimPatchOp; +import org.openmetadata.schema.api.scim.ScimUser; + +public interface ScimProvisioningService { + + Response listUsers(UriInfo uriInfo); + + Response createUser(ScimUser user, UriInfo uriInfo, SecurityContext securityContext); + + Response getUser(String id, UriInfo uriInfo); + + Response patchUser( + String id, ScimPatchOp request, UriInfo uriInfo, SecurityContext securityContext); + + Response updateUser(String id, ScimUser user, UriInfo uriInfo); + + Response deleteUser(String id, UriInfo uriInfo, SecurityContext securityContext); + + Response listGroups(UriInfo uriInfo); + + Response createGroup(ScimGroup group, UriInfo uriInfo, SecurityContext securityContext); + + Response updateGroup( + String id, ScimGroup group, UriInfo uriInfo, SecurityContext securityContext); + + Response deleteGroup(String id, UriInfo uriInfo, SecurityContext securityContext); + + Response getGroup(String id, UriInfo uriInfo); + + Response patchGroup( + String id, ScimPatchOp request, UriInfo uriInfo, SecurityContext securityContext); + + Response getSchemas(); + + Response getServiceProviderConfig(); +} diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/scim/impl/DefaultScimProvisioningService.java b/openmetadata-service/src/main/java/org/openmetadata/service/scim/impl/DefaultScimProvisioningService.java new file mode 100644 index 00000000000..f35e53941eb --- /dev/null +++ b/openmetadata-service/src/main/java/org/openmetadata/service/scim/impl/DefaultScimProvisioningService.java @@ -0,0 +1,91 @@ +package org.openmetadata.service.scim.impl; + +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.SecurityContext; +import jakarta.ws.rs.core.UriInfo; +import org.openmetadata.schema.api.scim.ScimGroup; +import org.openmetadata.schema.api.scim.ScimPatchOp; +import org.openmetadata.schema.api.scim.ScimUser; +import org.openmetadata.service.scim.ScimProvisioningService; + +public class DefaultScimProvisioningService implements ScimProvisioningService { + + private static final String MSG = "SCIM is not implemented in OpenMetadata."; + + private Response notImplemented() { + return Response.status(Response.Status.NOT_IMPLEMENTED).entity(MSG).build(); + } + + @Override + public Response listUsers(UriInfo uriInfo) { + return notImplemented(); + } + + @Override + public Response createUser(ScimUser user, UriInfo uriInfo, SecurityContext securityContext) { + return notImplemented(); + } + + @Override + public Response getUser(String id, UriInfo uriInfo) { + return notImplemented(); + } + + @Override + public Response patchUser( + String id, ScimPatchOp request, UriInfo uriInfo, SecurityContext securityContext) { + return notImplemented(); + } + + @Override + public Response updateUser(String id, ScimUser user, UriInfo uriInfo) { + return notImplemented(); + } + + @Override + public Response deleteUser(String id, UriInfo uriInfo, SecurityContext securityContext) { + return notImplemented(); + } + + @Override + public Response listGroups(UriInfo uriInfo) { + return notImplemented(); + } + + @Override + public Response createGroup(ScimGroup group, UriInfo uriInfo, SecurityContext securityContext) { + return notImplemented(); + } + + @Override + public Response updateGroup( + String id, ScimGroup group, UriInfo uriInfo, SecurityContext securityContext) { + return notImplemented(); + } + + @Override + public Response deleteGroup(String id, UriInfo uriInfo, SecurityContext securityContext) { + return notImplemented(); + } + + @Override + public Response getGroup(String id, UriInfo uriInfo) { + return notImplemented(); + } + + @Override + public Response patchGroup( + String id, ScimPatchOp request, UriInfo uriInfo, SecurityContext securityContext) { + return notImplemented(); + } + + @Override + public Response getSchemas() { + return notImplemented(); + } + + @Override + public Response getServiceProviderConfig() { + return notImplemented(); + } +} diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/security/SecurityUtil.java b/openmetadata-service/src/main/java/org/openmetadata/service/security/SecurityUtil.java index ffaddb84925..745ffdbbc3e 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/security/SecurityUtil.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/security/SecurityUtil.java @@ -100,7 +100,8 @@ public final class SecurityUtil { List jwtPrincipalClaimsOrder, Map claims) { String userName; - if (!nullOrEmpty(jwtPrincipalClaimsMapping)) { + + if (!nullOrEmpty(jwtPrincipalClaimsMapping) && !isBotW(claims)) { // We have a mapping available so we will use that String usernameClaim = jwtPrincipalClaimsMapping.get(USERNAME_CLAIM_KEY); String userNameClaimValue = getClaimOrObject(claims.get(usernameClaim)); @@ -122,7 +123,8 @@ public final class SecurityUtil { Map claims, String defaulPrincipalClaim) { String email; - if (!nullOrEmpty(jwtPrincipalClaimsMapping)) { + + if (!nullOrEmpty(jwtPrincipalClaimsMapping) && !isBotW(claims)) { // We have a mapping available so we will use that String emailClaim = jwtPrincipalClaimsMapping.get(EMAIL_CLAIM_KEY); String emailClaimValue = getClaimOrObject(claims.get(emailClaim)); @@ -192,7 +194,8 @@ public final class SecurityUtil { Set allowedDomains, boolean enforcePrincipalDomain) { String domain = StringUtils.EMPTY; - if (!nullOrEmpty(jwtPrincipalClaimsMapping)) { + + if (!nullOrEmpty(jwtPrincipalClaimsMapping) && !isBotW(claims)) { // We have a mapping available so we will use that String emailClaim = jwtPrincipalClaimsMapping.get(EMAIL_CLAIM_KEY); String emailClaimValue = getClaimOrObject(claims.get(emailClaim)); @@ -241,4 +244,9 @@ public final class SecurityUtil { public static boolean isBot(Map claims) { return claims.containsKey(BOT_CLAIM) && Boolean.TRUE.equals(claims.get(BOT_CLAIM).asBoolean()); } + + public static boolean isBotW(Map claims) { + Claim isBotClaim = (Claim) claims.get("isBot"); + return isBotClaim != null && Boolean.TRUE.equals(isBotClaim.asBoolean()); + } } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/util/UserUtil.java b/openmetadata-service/src/main/java/org/openmetadata/service/util/UserUtil.java index 38d71628c53..13edb58b5e0 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/util/UserUtil.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/util/UserUtil.java @@ -355,6 +355,8 @@ public final class UserUtil { .withUpdatedAt(System.currentTimeMillis()) .withTeams(EntityUtil.toEntityReferences(create.getTeams(), Entity.TEAM)) .withRoles(EntityUtil.toEntityReferences(create.getRoles(), Entity.ROLE)) - .withDomains(EntityUtil.getEntityReferences(Entity.DOMAIN, create.getDomains())); + .withDomains(EntityUtil.getEntityReferences(Entity.DOMAIN, create.getDomains())) + .withExternalId(create.getExternalId()) + .withScimUserName(create.getScimUserName()); } } diff --git a/openmetadata-service/src/main/resources/json/data/bot/scimBot.json b/openmetadata-service/src/main/resources/json/data/bot/scimBot.json new file mode 100644 index 00000000000..a29a05e178d --- /dev/null +++ b/openmetadata-service/src/main/resources/json/data/bot/scimBot.json @@ -0,0 +1,11 @@ +{ + "name": "scim-bot", + "displayName": "SCIM Bot", + "description": "Bot with SCIM-only access", + "fullyQualifiedName": "scim-bot", + "botUser": { + "name" : "scim-bot", + "type" : "user" + }, + "provider": "system" +} diff --git a/openmetadata-service/src/main/resources/json/data/botUser/scimBot.json b/openmetadata-service/src/main/resources/json/data/botUser/scimBot.json new file mode 100644 index 00000000000..5261523512a --- /dev/null +++ b/openmetadata-service/src/main/resources/json/data/botUser/scimBot.json @@ -0,0 +1,9 @@ +{ + "name": "scim-bot", + "roles": [ + { + "name": "ScimBotRole", + "type": "role" + } + ] +} \ No newline at end of file diff --git a/openmetadata-service/src/main/resources/json/data/policy/ScimBotPolicy.json b/openmetadata-service/src/main/resources/json/data/policy/ScimBotPolicy.json new file mode 100644 index 00000000000..4fda08a0f25 --- /dev/null +++ b/openmetadata-service/src/main/resources/json/data/policy/ScimBotPolicy.json @@ -0,0 +1,18 @@ +{ + "name": "ScimBotPolicy", + "displayName": "SCIM Bot Policy", + "fullyQualifiedName": "ScimBotPolicy", + "description": "Policy to allow SCIM bot access only to SCIM APIs and block all others.", + "enabled": true, + "allowDelete": false, + "provider": "system", + "rules": [ + { + "name": "Allow-Scim-Endpoints", + "description": "Allow access to SCIM endpoints", + "resources": ["scim"], + "operations": ["CreateScim", "EditScim", "ViewScim", "DeleteScim"], + "effect": "allow" + } + ] +} diff --git a/openmetadata-service/src/main/resources/json/data/role/ScimBotRole.json b/openmetadata-service/src/main/resources/json/data/role/ScimBotRole.json new file mode 100644 index 00000000000..934dce3650b --- /dev/null +++ b/openmetadata-service/src/main/resources/json/data/role/ScimBotRole.json @@ -0,0 +1,13 @@ +{ + "name": "ScimBotRole", + "displayName": "SCIM bot role", + "description": "Role corresponding to the SCIM bot, with SCIM-only access.", + "allowDelete": false, + "provider": "system", + "policies": [ + { + "type": "policy", + "name": "ScimBotPolicy" + } + ] +} diff --git a/openmetadata-spec/src/main/resources/json/schema/api/scim/scimGroup.json b/openmetadata-spec/src/main/resources/json/schema/api/scim/scimGroup.json new file mode 100644 index 00000000000..25cec6a67d6 --- /dev/null +++ b/openmetadata-spec/src/main/resources/json/schema/api/scim/scimGroup.json @@ -0,0 +1,78 @@ +{ + "$id": "https://open-metadata.org/schema/api/scimGroup.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ScimGroup", + "description": "SCIM-compliant Group object", + "type": "object", + "javaType": "org.openmetadata.schema.api.scim.ScimGroup", + "properties": { + "schemas": { + "type": "array", + "description": "SCIM schemas used for this resource", + "items": { + "type": "string" + }, + "default": ["urn:ietf:params:scim:schemas:core:2.0:Group"] + }, + "id": { + "type": "string", + "description": "Unique identifier for the group" + }, + "displayName": { + "type": "string", + "description": "Human-readable name of the group" + }, + "externalId": { + "type": "string", + "description": "External system identifier" + }, + "active": { + "type": "boolean", + "description": "Whether the group is active" + }, + "members": { + "type": "array", + "description": "Members of the group", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string", + "description": "ID of the member (user)" + }, + "display": { + "type": "string", + "description": "Display name of the member" + }, + "type": { + "type": "string", + "description": "Type of member - typically 'User'" + } + }, + "required": ["value"] + } + }, + "meta": { + "type": "object", + "description": "Metadata about the group", + "properties": { + "resourceType": { + "type": "string" + }, + "created": { + "type": "string", + "format": "date-time" + }, + "lastModified": { + "type": "string", + "format": "date-time" + }, + "location": { + "type": "string" + } + } + } + }, + "required": ["schemas", "displayName"], + "additionalProperties": true +} diff --git a/openmetadata-spec/src/main/resources/json/schema/api/scim/scimPatchOp.json b/openmetadata-spec/src/main/resources/json/schema/api/scim/scimPatchOp.json new file mode 100644 index 00000000000..7257f16d1aa --- /dev/null +++ b/openmetadata-spec/src/main/resources/json/schema/api/scim/scimPatchOp.json @@ -0,0 +1,39 @@ +{ + "$id": "https://open-metadata.org/schema/api/scimPatchOp.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ScimPatchOp", + "description": "SCIM PatchOp request as per RFC 7644", + "type": "object", + "javaType": "org.openmetadata.schema.api.scim.ScimPatchOp", + "properties": { + "schemas": { + "type": "array", + "items": { + "type": "string" + }, + "default": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"] + }, + "Operations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "op": { + "type": "string", + "enum": ["add", "replace", "remove"], + "javaType": "java.lang.String" + }, + "path": { + "type": "string" + }, + "value": { + "type": ["object", "array", "string", "boolean", "number"] + } + }, + "required": ["op"] + } + } + }, + "required": ["schemas", "Operations"], + "additionalProperties": false +} diff --git a/openmetadata-spec/src/main/resources/json/schema/api/scim/scimUser.json b/openmetadata-spec/src/main/resources/json/schema/api/scim/scimUser.json new file mode 100644 index 00000000000..54bc691d6e7 --- /dev/null +++ b/openmetadata-spec/src/main/resources/json/schema/api/scim/scimUser.json @@ -0,0 +1,92 @@ +{ + "$id": "https://open-metadata.org/schema/api/scimUser.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ScimUser", + "description": "SCIM-compliant User object", + "type": "object", + "javaType": "org.openmetadata.schema.api.scim.ScimUser", + "properties": { + "schemas": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { "type": "string" }, + "externalId": { "type": "string" }, + "userName": { "type": "string" }, + "displayName": { "type": "string" }, + "active": { "type": "boolean", "default": true }, + "title": { "type": "string" }, + "preferredLanguage": { "type": "string" }, + "emails": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { "type": "string", "format": "email" }, + "type": { "type": "string" }, + "primary": { "type": "boolean" } + } + } + }, + "phoneNumbers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { "type": "string" }, + "type": { "type": "string" } + } + } + }, + "addresses": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { "type": "string" }, + "formatted": { "type": "string" }, + "streetAddress": { "type": "string" }, + "locality": { "type": "string" }, + "region": { "type": "string" }, + "postalCode": { "type": "string" }, + "country": { "type": "string" } + } + } + }, + "name": { + "type": "object", + "properties": { + "givenName": { "type": "string" }, + "familyName": { "type": "string" }, + "formatted": { "type": "string" } + } + }, + "meta": { + "type": "object", + "properties": { + "resourceType": { "type": "string" }, + "created": { "type": "string", "format": "date-time" }, + "lastModified": { "type": "string", "format": "date-time" }, + "location": { "type": "string" } + }, + "additionalProperties": true + }, + "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": { + "type": "object", + "properties": { + "employeeId": { "type": "string" }, + "department": { "type": "string" }, + "manager": { + "type": "object", + "properties": { + "value": { "type": "string" }, + "displayName": { "type": "string" } + } + } + } + } + }, + "additionalProperties": true +} diff --git a/openmetadata-spec/src/main/resources/json/schema/api/teams/createTeam.json b/openmetadata-spec/src/main/resources/json/schema/api/teams/createTeam.json index 2ee198f369f..45bf6dcacd8 100644 --- a/openmetadata-spec/src/main/resources/json/schema/api/teams/createTeam.json +++ b/openmetadata-spec/src/main/resources/json/schema/api/teams/createTeam.json @@ -23,6 +23,10 @@ "description": "Optional name used for display purposes. Example 'Marketing Team'.", "type": "string" }, + "externalId": { + "description": "External identifier for the team from an external identity provider (e.g., Azure AD group ID).", + "type": "string" + }, "description": { "description": "Optional description of the team.", "$ref": "../../type/basic.json#/definitions/markdown" diff --git a/openmetadata-spec/src/main/resources/json/schema/api/teams/createUser.json b/openmetadata-spec/src/main/resources/json/schema/api/teams/createUser.json index 8264e0b413d..0f20199cd21 100644 --- a/openmetadata-spec/src/main/resources/json/schema/api/teams/createUser.json +++ b/openmetadata-spec/src/main/resources/json/schema/api/teams/createUser.json @@ -18,6 +18,14 @@ "description": "Name used for display purposes. Example 'FirstName LastName'", "type": "string" }, + "externalId": { + "description": "External identifier from identity provider (used for SCIM).", + "type": "string" + }, + "scimUserName": { + "description": "Raw user name from SCIM.", + "type": "string" + }, "email": { "$ref": "../../type/basic.json#/definitions/email" }, diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/policies/accessControl/resourceDescriptor.json b/openmetadata-spec/src/main/resources/json/schema/entity/policies/accessControl/resourceDescriptor.json index 70e6703a494..0ffcb4f713c 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/policies/accessControl/resourceDescriptor.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/policies/accessControl/resourceDescriptor.json @@ -53,7 +53,11 @@ "Deploy", "Trigger", "Kill", - "GenerateToken" + "GenerateToken", + "EditScim", + "CreateScim", + "DeleteScim", + "ViewScim" ] } }, diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/teams/team.json b/openmetadata-spec/src/main/resources/json/schema/entity/teams/team.json index d37c656fa56..85e5db8d9bf 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/teams/team.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/teams/team.json @@ -44,6 +44,10 @@ "description": "Name used for display purposes. Example 'Data Science team'.", "type": "string" }, + "externalId": { + "description": "External identifier for the team from an external identity provider (e.g., Azure AD group ID).", + "type": "string" + }, "description": { "description": "Description of the team.", "$ref": "../../type/basic.json#/definitions/markdown" diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/teams/user.json b/openmetadata-spec/src/main/resources/json/schema/entity/teams/user.json index 4cc48a57264..486a9cc3831 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/teams/user.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/teams/user.json @@ -49,6 +49,14 @@ "description": "Used for user biography.", "$ref": "../../type/basic.json#/definitions/markdown" }, + "externalId": { + "description": "External identifier from identity provider (used for SCIM).", + "type": "string" + }, + "scimUserName": { + "description": "Raw user name from SCIM.", + "type": "string" + }, "displayName": { "description": "Name used for display purposes. Example 'FirstName LastName'.", "type": "string" diff --git a/openmetadata-spec/src/main/resources/json/schema/scim/scimConfiguration.json b/openmetadata-spec/src/main/resources/json/schema/scim/scimConfiguration.json new file mode 100644 index 00000000000..1549363e2f9 --- /dev/null +++ b/openmetadata-spec/src/main/resources/json/schema/scim/scimConfiguration.json @@ -0,0 +1,23 @@ +{ + "$id": "https://open-metadata.org/schema/security/scim/scimConfiguration.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "SCIM Configuration", + "description": "SCIM configuration for automatic provisioning through identity providers like Azure AD or Okta.", + "type": "object", + "javaType": "org.openmetadata.schema.security.scim.ScimConfiguration", + "properties": { + "enabled": { + "title": "Enabled", + "description": "Whether SCIM provisioning is enabled.", + "type": "boolean", + "default": false + }, + "identityProvider": { + "title": "Identity Provider", + "description": "The name of the identity provider for SCIM (e.g., azure, okta).", + "type": "string", + "default": "azure" + } + }, + "additionalProperties": false +} diff --git a/openmetadata-spec/src/main/resources/json/schema/settings/settings.json b/openmetadata-spec/src/main/resources/json/schema/settings/settings.json index 94de06ff7a6..775a959fca5 100644 --- a/openmetadata-spec/src/main/resources/json/schema/settings/settings.json +++ b/openmetadata-spec/src/main/resources/json/schema/settings/settings.json @@ -34,7 +34,8 @@ "searchSettings", "assetCertificationSettings", "lineageSettings", - "workflowSettings" + "workflowSettings", + "scimConfiguration" ] } }, diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/api/scim/scimGroup.ts b/openmetadata-ui/src/main/resources/ui/src/generated/api/scim/scimGroup.ts new file mode 100644 index 00000000000..f2820742fb9 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/generated/api/scim/scimGroup.ts @@ -0,0 +1,73 @@ +/* + * Copyright 2025 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * SCIM-compliant Group object + */ +export interface ScimGroup { + /** + * Whether the group is active + */ + active?: boolean; + /** + * Human-readable name of the group + */ + displayName: string; + /** + * External system identifier + */ + externalId?: string; + /** + * Unique identifier for the group + */ + id?: string; + /** + * Members of the group + */ + members?: Member[]; + /** + * Metadata about the group + */ + meta?: Meta; + /** + * SCIM schemas used for this resource + */ + schemas: string[]; + [property: string]: any; +} + +export interface Member { + /** + * Display name of the member + */ + display?: string; + /** + * Type of member - typically 'User' + */ + type?: string; + /** + * ID of the member (user) + */ + value: string; + [property: string]: any; +} + +/** + * Metadata about the group + */ +export interface Meta { + created?: Date; + lastModified?: Date; + location?: string; + resourceType?: string; + [property: string]: any; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/api/scim/scimPatchOp.ts b/openmetadata-ui/src/main/resources/ui/src/generated/api/scim/scimPatchOp.ts new file mode 100644 index 00000000000..5882c22636c --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/generated/api/scim/scimPatchOp.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2025 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * SCIM PatchOp request as per RFC 7644 + */ +export interface ScimPatchOp { + Operations: Operation[]; + schemas: string[]; +} + +export interface Operation { + op: Op; + path?: string; + value?: any[] | boolean | number | { [key: string]: any } | string; + [property: string]: any; +} + +export enum Op { + Add = "add", + Remove = "remove", + Replace = "replace", +} diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/api/scim/scimUser.ts b/openmetadata-ui/src/main/resources/ui/src/generated/api/scim/scimUser.ts new file mode 100644 index 00000000000..feea7c03264 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/generated/api/scim/scimUser.ts @@ -0,0 +1,84 @@ +/* + * Copyright 2025 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * SCIM-compliant User object + */ +export interface ScimUser { + active?: boolean; + addresses?: Address[]; + displayName?: string; + emails?: Email[]; + externalId?: string; + id?: string; + meta?: Meta; + name?: Name; + phoneNumbers?: PhoneNumber[]; + preferredLanguage?: string; + schemas?: string[]; + title?: string; + "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"?: UrnIETFParamsScimSchemasExtensionEnterprise20User; + userName?: string; + [property: string]: any; +} + +export interface Address { + country?: string; + formatted?: string; + locality?: string; + postalCode?: string; + region?: string; + streetAddress?: string; + type?: string; + [property: string]: any; +} + +export interface Email { + primary?: boolean; + type?: string; + value?: string; + [property: string]: any; +} + +export interface Meta { + created?: Date; + lastModified?: Date; + location?: string; + resourceType?: string; + [property: string]: any; +} + +export interface Name { + familyName?: string; + formatted?: string; + givenName?: string; + [property: string]: any; +} + +export interface PhoneNumber { + type?: string; + value?: string; + [property: string]: any; +} + +export interface UrnIETFParamsScimSchemasExtensionEnterprise20User { + department?: string; + employeeId?: string; + manager?: Manager; + [property: string]: any; +} + +export interface Manager { + displayName?: string; + value?: string; + [property: string]: any; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/api/teams/createTeam.ts b/openmetadata-ui/src/main/resources/ui/src/generated/api/teams/createTeam.ts index 77eb3635132..25a9ecd3a57 100644 --- a/openmetadata-ui/src/main/resources/ui/src/generated/api/teams/createTeam.ts +++ b/openmetadata-ui/src/main/resources/ui/src/generated/api/teams/createTeam.ts @@ -41,6 +41,11 @@ export interface CreateTeam { * Email address of the team. */ email?: string; + /** + * External identifier for the team from an external identity provider (e.g., Azure AD group + * ID). + */ + externalId?: string; /** * Can any user join this team during sign up? Value of true indicates yes, and false no. */ diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/api/teams/createUser.ts b/openmetadata-ui/src/main/resources/ui/src/generated/api/teams/createUser.ts index cdaf77a10a6..b418e55d3eb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/generated/api/teams/createUser.ts +++ b/openmetadata-ui/src/main/resources/ui/src/generated/api/teams/createUser.ts @@ -47,6 +47,10 @@ export interface CreateUser { */ domains?: string[]; email: string; + /** + * External identifier from identity provider (used for SCIM). + */ + externalId?: string; /** * When true indicates user is an administrator for the system with superuser privileges */ @@ -72,6 +76,10 @@ export interface CreateUser { * Roles that the user has been assigned */ roles?: string[]; + /** + * Raw user name from SCIM. + */ + scimUserName?: string; /** * Teams that the user belongs to */ diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/entity/policies/accessControl/resourceDescriptor.ts b/openmetadata-ui/src/main/resources/ui/src/generated/entity/policies/accessControl/resourceDescriptor.ts index 2b0c439782e..c5923c07b96 100644 --- a/openmetadata-ui/src/main/resources/ui/src/generated/entity/policies/accessControl/resourceDescriptor.ts +++ b/openmetadata-ui/src/main/resources/ui/src/generated/entity/policies/accessControl/resourceDescriptor.ts @@ -32,7 +32,9 @@ export enum Operation { All = "All", Create = "Create", CreateIngestionPipelineAutomator = "CreateIngestionPipelineAutomator", + CreateScim = "CreateScim", Delete = "Delete", + DeleteScim = "DeleteScim", DeleteTestCaseFailedRowsSample = "DeleteTestCaseFailedRowsSample", Deploy = "Deploy", EditAll = "EditAll", @@ -53,6 +55,7 @@ export enum Operation { EditReviewers = "EditReviewers", EditRole = "EditRole", EditSampleData = "EditSampleData", + EditScim = "EditScim", EditStatus = "EditStatus", EditTags = "EditTags", EditTeams = "EditTeams", @@ -69,6 +72,7 @@ export enum Operation { ViewProfilerGlobalConfiguration = "ViewProfilerGlobalConfiguration", ViewQueries = "ViewQueries", ViewSampleData = "ViewSampleData", + ViewScim = "ViewScim", ViewTestCaseFailedRowsSample = "ViewTestCaseFailedRowsSample", ViewTests = "ViewTests", ViewUsage = "ViewUsage", diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/entity/teams/team.ts b/openmetadata-ui/src/main/resources/ui/src/generated/entity/teams/team.ts index c5dea9f0fe1..835c047acd6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/generated/entity/teams/team.ts +++ b/openmetadata-ui/src/main/resources/ui/src/generated/entity/teams/team.ts @@ -56,6 +56,11 @@ export interface Team { * Email address of the team. */ email?: string; + /** + * External identifier for the team from an external identity provider (e.g., Azure AD group + * ID). + */ + externalId?: string; /** * FullyQualifiedName same as `name`. */ diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/entity/teams/user.ts b/openmetadata-ui/src/main/resources/ui/src/generated/entity/teams/user.ts index 2d0362081fc..f50858fd647 100644 --- a/openmetadata-ui/src/main/resources/ui/src/generated/entity/teams/user.ts +++ b/openmetadata-ui/src/main/resources/ui/src/generated/entity/teams/user.ts @@ -45,6 +45,10 @@ export interface User { * Email address of the user. */ email: string; + /** + * External identifier from identity provider (used for SCIM). + */ + externalId?: string; /** * List of entities followed by the user. */ @@ -107,6 +111,10 @@ export interface User { * Roles that the user has been assigned. */ roles?: EntityReference[]; + /** + * Raw user name from SCIM. + */ + scimUserName?: string; /** * Teams that the user belongs to. */ diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/scim/scimConfiguration.ts b/openmetadata-ui/src/main/resources/ui/src/generated/scim/scimConfiguration.ts new file mode 100644 index 00000000000..1f57a75e565 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/generated/scim/scimConfiguration.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2025 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * SCIM configuration for automatic provisioning through identity providers like Azure AD or + * Okta. + */ +export interface ScimConfiguration { + /** + * Whether SCIM provisioning is enabled. + */ + enabled?: boolean; + /** + * The name of the identity provider for SCIM (e.g., azure, okta). + */ + identityProvider?: string; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/settings/settings.ts b/openmetadata-ui/src/main/resources/ui/src/generated/settings/settings.ts index 5d859ceaf5c..02b27d0eca0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/generated/settings/settings.ts +++ b/openmetadata-ui/src/main/resources/ui/src/generated/settings/settings.ts @@ -43,6 +43,7 @@ export enum SettingType { OpenMetadataBaseURLConfiguration = "openMetadataBaseUrlConfiguration", ProfilerConfiguration = "profilerConfiguration", SandboxModeEnabled = "sandboxModeEnabled", + ScimConfiguration = "scimConfiguration", SearchSettings = "searchSettings", SecretsManagerConfiguration = "secretsManagerConfiguration", SlackAppConfiguration = "slackAppConfiguration",