Skip to content

Commit

Permalink
fix(config-api): SAML TR metadata validation, user mgt spec for error…
Browse files Browse the repository at this point in the history
… and security issue (#7930)

* feat(config-api): saml plugin changes for new fields

Signed-off-by: pujavs <[email protected]>

* feat(config-api): kc link plugin endpoint

Signed-off-by: pujavs <[email protected]>

* feat(config-api): kc link plugin endpoint

Signed-off-by: pujavs <[email protected]>

* feat(config-api): kc link plugin endpoint

Signed-off-by: pujavs <[email protected]>

* feat(config-api): saml plugin changes

Signed-off-by: pujavs <[email protected]>

* feat(config-api): kc plugin changes

Signed-off-by: pujavs <[email protected]>

* feat(config-api): saml plugin changes for metadata elements

Signed-off-by: pujavs <[email protected]>

* feat(config-api): resolved merge conflict

Signed-off-by: pujavs <[email protected]>

* feat(config-api): resolved merge conflict

Signed-off-by: pujavs <[email protected]>

* feat(config-api): saml metedata elements save

Signed-off-by: pujavs <[email protected]>

* feat(config-api): attribute validation check

Signed-off-by: pujavs <[email protected]>

* feat(config-api): attribute validation in schema

Signed-off-by: pujavs <[email protected]>

* feat(config-api): custom attribute verification in schema

Signed-off-by: pujavs <[email protected]>

* feat(config-api): attribute check in schema name and client password decryption handling

Signed-off-by: pujavs <[email protected]>

* feat(config-api): saml plugin changes for sp metadata handling

Signed-off-by: pujavs <[email protected]>

* feat(config-api): saml plugin changes for sp metadata handling

Signed-off-by: pujavs <[email protected]>

* feat(config-api): SAML TR enhacement for metadata field and filename

Signed-off-by: pujavs <[email protected]>

* feat(config-api): SAML TR enhacement for metadata field and filename

Signed-off-by: pujavs <[email protected]>

* feat(config-api): security issue for apache-mime4j-core

Signed-off-by: pujavs <[email protected]>

* feat(config-api): exception handling for user creation

Signed-off-by: pujavs <[email protected]>

* fix(config-api): SAML TR metadata validation, user mgt spec for error and security issue

Signed-off-by: pujavs <[email protected]>

* fix(config-api): SAML TR metadata validation, user mgt spec for error and security issue

Signed-off-by: pujavs <[email protected]>

---------

Signed-off-by: pujavs <[email protected]>
Co-authored-by: YuriyZ <[email protected]>
  • Loading branch information
pujavs and yuriyz authored Mar 1, 2024
1 parent 51d2f2f commit b22b0a4
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 26 deletions.
12 changes: 6 additions & 6 deletions jans-config-api/docs/jans-config-api-swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7898,18 +7898,18 @@ components:
type: boolean
whitePagesCanView:
type: boolean
adminCanView:
type: boolean
adminCanEdit:
type: boolean
userCanEdit:
type: boolean
adminCanView:
type: boolean
userCanView:
type: boolean
userCanAccess:
type: boolean
adminCanAccess:
type: boolean
userCanAccess:
type: boolean
baseDn:
type: string
PatchRequest:
Expand Down Expand Up @@ -10180,10 +10180,10 @@ components:
ttl:
type: integer
format: int32
persisted:
type: boolean
opbrowserState:
type: string
persisted:
type: boolean
SessionIdAccessMap:
type: object
properties:
Expand Down
44 changes: 41 additions & 3 deletions jans-config-api/plugins/docs/kc-saml-plugin-swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -686,10 +686,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/TrustRelationship'
"400":
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
"401":
description: Unauthorized
"404":
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
"500":
description: InternalServerError
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
security:
- oauth2:
- https://jans.io/oauth/config/saml.write
Expand All @@ -716,10 +732,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/TrustRelationship'
"400":
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
"401":
description: Unauthorized
"404":
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
"500":
description: InternalServerError
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
security:
- oauth2:
- https://jans.io/oauth/config/saml.write
Expand Down Expand Up @@ -1070,10 +1102,7 @@ components:
type: string
enum:
- file
- uri
- federation
- manual
- mdq
samlMetadata:
$ref: '#/components/schemas/SAMLMetadata'
redirectUris:
Expand Down Expand Up @@ -1129,6 +1158,15 @@ components:
metaDataFile:
type: string
format: binary
ApiError:
type: object
properties:
code:
type: string
message:
type: string
description:
type: string
securitySchemes:
oauth2:
type: oauth2
Expand Down
35 changes: 35 additions & 0 deletions jans-config-api/plugins/docs/user-mgt-plugin-swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -324,12 +324,24 @@ paths:
}
"400":
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
"401":
description: Unauthorized
"404":
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
"500":
description: InternalServerError
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
security:
- oauth2:
- https://jans.io/oauth/config/user.write
Expand Down Expand Up @@ -528,10 +540,24 @@ paths:
}
"400":
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
"401":
description: Unauthorized
"404":
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
"500":
description: InternalServerError
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
security:
- oauth2:
- https://jans.io/oauth/config/user.write
Expand Down Expand Up @@ -882,6 +908,15 @@ components:
type: string
baseDn:
type: string
ApiError:
type: object
properties:
code:
type: string
message:
type: string
description:
type: string
UserPagedResult:
type: object
properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/
public enum MetadataSourceType implements AttributeEnum {

FILE("file", "File",1), URI("uri", "URI",2), FEDERATION("federation", "Federation",3), MANUAL("manual", "Manual",4), MDQ("mdq", "MDQ",5);
FILE("file", "File",1), MANUAL("manual", "Manual",2);

private final String value;
private final String displayName;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package io.jans.configapi.plugin.saml.rest;

import static io.jans.as.model.util.Util.escapeLog;
import io.jans.configapi.plugin.saml.model.TrustRelationship;
import io.jans.configapi.plugin.saml.form.TrustRelationshipForm;
import io.jans.configapi.core.model.ApiError;
import io.jans.configapi.core.rest.BaseResource;
import io.jans.configapi.core.rest.ProtectedApi;
import io.jans.configapi.plugin.saml.model.MetadataSourceType;
import io.jans.configapi.plugin.saml.model.TrustRelationship;
import io.jans.configapi.plugin.saml.form.TrustRelationshipForm;
import io.jans.configapi.plugin.saml.util.Constants;
import io.jans.configapi.util.AttributeNames;
import io.jans.configapi.plugin.saml.service.SamlService;
Expand All @@ -31,6 +33,7 @@
import java.util.*;
import java.util.stream.*;

import org.apache.commons.lang.StringUtils;
import org.jboss.resteasy.annotations.providers.multipart.MultipartForm;
import org.slf4j.Logger;

Expand All @@ -44,6 +47,8 @@ public class TrustRelationshipResource extends BaseResource {
private static final String SAML_TRUST_RELATIONSHIP_CHECK_STR = "Trust Relationship identified by '";
private static final String NAME_CONFLICT = "NAME_CONFLICT";
private static final String NAME_CONFLICT_MSG = "Trust Relationship with same name `%s` already exists!";
private static final String DATA_NULL_CHK = "RESOURCE_IS_NULL";
private static final String DATA_NULL_MSG = "`%s` should not be null!";

@Inject
Logger logger;
Expand Down Expand Up @@ -95,8 +100,11 @@ public Response getTrustRelationshipById(
@RequestBody(description = "Trust Relationship object", content = @Content(mediaType = MediaType.MULTIPART_FORM_DATA, schema = @Schema(implementation = TrustRelationshipForm.class), examples = @ExampleObject(name = "Request example", value = "example/trust-relationship/trust-relationship-post.json")))
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "Newly created Trust Relationship", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = TrustRelationship.class))),
@ApiResponse(responseCode = "400", description = "Bad Request" , content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApiError.class, description = "BadRequestException"))),
@ApiResponse(responseCode = "401", description = "Unauthorized"),
@ApiResponse(responseCode = "500", description = "InternalServerError") })
@ApiResponse(responseCode = "404", description = "Not Found" , content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApiError.class, description = "NotFoundException"))),
@ApiResponse(responseCode = "500", description = "InternalServerError", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApiError.class, description = "InternalServerError"))),
})
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("/upload")
@ProtectedApi(scopes = { Constants.SAML_WRITE_ACCESS }, groupScopes = {}, superScopes = {
Expand Down Expand Up @@ -127,6 +135,7 @@ public Response createTrustRelationshipWithFile(@MultipartForm TrustRelationship
logger.debug(" Create metaDataFile.available():{}", metaDataFile.available());
}

validateSpMetaDataSourceType(trustRelationship, metaDataFile);
String inum = samlService.generateInumForNewRelationship();
trustRelationship.setInum(inum);
trustRelationship.setDn(samlService.getDnForTrustRelationship(inum));
Expand All @@ -143,8 +152,11 @@ public Response createTrustRelationshipWithFile(@MultipartForm TrustRelationship
@RequestBody(description = "Trust Relationship object", content = @Content(mediaType = MediaType.MULTIPART_FORM_DATA, schema = @Schema(implementation = TrustRelationshipForm.class), examples = @ExampleObject(name = "Request example", value = "example/trust-relationship/trust-relationship-put.json")))
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = TrustRelationship.class))),
@ApiResponse(responseCode = "400", description = "Bad Request" , content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApiError.class, description = "BadRequestException"))),
@ApiResponse(responseCode = "401", description = "Unauthorized"),
@ApiResponse(responseCode = "500", description = "InternalServerError") })
@ApiResponse(responseCode = "404", description = "Not Found" , content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApiError.class, description = "NotFoundException"))),
@ApiResponse(responseCode = "500", description = "InternalServerError", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApiError.class, description = "InternalServerError"))),
})
@ProtectedApi(scopes = { Constants.SAML_WRITE_ACCESS })
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("/upload")
Expand Down Expand Up @@ -193,8 +205,8 @@ public Response updateTrustRelationship(@MultipartForm TrustRelationshipForm tru
if (metaDataFile != null) {
logger.debug(" Create metaDataFile.available():{}", metaDataFile.available());
}


validateSpMetaDataSourceType(trustRelationship, metaDataFile);
// Update
trustRelationship = samlService.updateTrustRelationship(trustRelationship);

Expand Down Expand Up @@ -245,5 +257,45 @@ public Response processMetadataFiles() {

return Response.ok().build();
}

private void validateSpMetaDataSourceType(TrustRelationship trustRelationship, InputStream metaDataFile)
throws IOException {
logger.info("Validate SP MetaDataSourceType trustRelationship:{}, metaDataFile:{}", trustRelationship,
metaDataFile);

checkResourceNotNull(trustRelationship.getSpMetaDataSourceType(), "SP MetaData Source Type");

logger.info("Validate trustRelationship.getSpMetaDataSourceType():{}",
trustRelationship.getSpMetaDataSourceType());

if (trustRelationship.getSpMetaDataSourceType().equals(MetadataSourceType.FILE)) {

if (metaDataFile == null || metaDataFile.available() <= 0) {
throwBadRequestException(DATA_NULL_CHK, String.format(DATA_NULL_MSG, "SP MetaData File"));
}

// Since SP Metadata source is File set SamlMetadata manual elements to null
trustRelationship.setSamlMetadata(null);

} else if (trustRelationship.getSpMetaDataSourceType().equals(MetadataSourceType.MANUAL)) {

if (metaDataFile != null && metaDataFile.available() > 0) {
throwBadRequestException("SP MetaData File should not be provided!");
}

checkResourceNotNull(trustRelationship.getSamlMetadata(), "'SamlMetadata manual elements'");
checkNotNull(trustRelationship.getSamlMetadata().getEntityId(), "'EntityId'");
checkNotNull(trustRelationship.getSamlMetadata().getNameIDPolicyFormat(),
"'NameIDPolicyFormat'");
checkNotNull(trustRelationship.getSamlMetadata().getSingleLogoutServiceUrl(),
"'SingleLogoutServiceUrl'");
if (StringUtils.isBlank(trustRelationship.getSamlMetadata().getJansAssertionConsumerServiceGetURL())
&& (StringUtils
.isBlank(trustRelationship.getSamlMetadata().getJansAssertionConsumerServiceGetURL()))) {
throwBadRequestException("Either of AssertionConsumerService GET or POST URL should be provided!");
}
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.github.fge.jsonpatch.JsonPatchException;
import io.jans.as.common.model.common.User;
import io.jans.configapi.core.model.ApiError;
import io.jans.configapi.core.rest.BaseResource;
import io.jans.configapi.core.rest.ProtectedApi;
import io.jans.configapi.plugin.mgt.model.user.CustomUser;
Expand Down Expand Up @@ -134,9 +135,11 @@ public Response getUserByInum(
@RequestBody(description = "User object", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class), examples = @ExampleObject(name = "Request json example", value = "example/user/user-post.json")))
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "Created", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class, description = "Created Object"), examples = @ExampleObject(name = "Response json example", value = "example/user/user.json"))),
@ApiResponse(responseCode = "400", description = "Bad Request"),
@ApiResponse(responseCode = "400", description = "Bad Request" , content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApiError.class, description = "BadRequestException"))),
@ApiResponse(responseCode = "401", description = "Unauthorized"),
@ApiResponse(responseCode = "500", description = "InternalServerError") })
@ApiResponse(responseCode = "404", description = "Not Found" , content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApiError.class, description = "NotFoundException"))),
@ApiResponse(responseCode = "500", description = "InternalServerError", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApiError.class, description = "InternalServerError"))),
})
@POST
@ProtectedApi(scopes = { ApiAccessConstants.USER_WRITE_ACCESS })
public Response createUser(@Valid CustomUser customUser,
Expand All @@ -147,6 +150,7 @@ public Response createUser(@Valid CustomUser customUser,
removeNonLDAPAttributes);
}

try {
// get User object
User user = setUserAttributes(customUser);

Expand All @@ -168,6 +172,10 @@ public Response createUser(@Valid CustomUser customUser,
// get custom user
customUser = getCustomUser(user, removeNonLDAPAttributes);
logger.info("newly created customUser:{}", customUser);
}catch(WebApplicationException waex) {
logger.error("ApplicationException while creating user is:", waex);
throwInternalServerException("USER_CREATION", waex.getMessage());
}

return Response.status(Response.Status.CREATED).entity(customUser).build();
}
Expand All @@ -178,10 +186,11 @@ public Response createUser(@Valid CustomUser customUser,
@RequestBody(description = "User object", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class), examples = @ExampleObject(name = "Request json example", value = "example/user/user.json")))
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CustomUser.class), examples = @ExampleObject(name = "Response json example", value = "example/user/user.json"))),
@ApiResponse(responseCode = "400", description = "Bad Request"),
@ApiResponse(responseCode = "400", description = "Bad Request" , content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApiError.class, description = "BadRequestException"))),
@ApiResponse(responseCode = "401", description = "Unauthorized"),
@ApiResponse(responseCode = "404", description = "Not Found"),
@ApiResponse(responseCode = "500", description = "InternalServerError") })
@ApiResponse(responseCode = "404", description = "Not Found" , content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApiError.class, description = "NotFoundException"))),
@ApiResponse(responseCode = "500", description = "InternalServerError", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApiError.class, description = "InternalServerError"))),
})
@PUT
@ProtectedApi(scopes = { ApiAccessConstants.USER_WRITE_ACCESS })
public Response updateUser(@Valid CustomUser customUser,
Expand Down
2 changes: 1 addition & 1 deletion jans-config-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
<swagger-maven-plugin-jakarta>2.2.19</swagger-maven-plugin-jakarta>
<swagger-models-jakarta>2.2.7</swagger-models-jakarta>

<apache.james.version>0.8.9</apache.james.version>
<apache.james.version>0.8.10</apache.james.version>
</properties>

<prerequisites>
Expand Down
Loading

0 comments on commit b22b0a4

Please sign in to comment.