Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.dotcms.filters.interceptor.saml.SamlWebUtils;
import com.dotcms.rest.WebResource;
import com.dotcms.rest.annotation.NoCache;
import com.dotcms.rest.annotation.SwaggerCompliant;
import com.dotcms.saml.Attributes;
import com.dotcms.saml.DotSamlConstants;
import com.dotcms.saml.DotSamlException;
Expand All @@ -21,6 +22,12 @@
import com.dotmarketing.util.WebKeys;
import com.google.common.annotations.VisibleForTesting;
import com.liferay.portal.model.User;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.glassfish.jersey.server.JSONP;

Expand Down Expand Up @@ -49,6 +56,7 @@
* - metadata renders the XML metadata.
* @author jsanca
*/
@SwaggerCompliant(value = "Core authentication and user management APIs", batch = 1)
@Tag(name = "SAML Authentication")
@Path("/v1/dotsaml")
public class DotSamlResource implements Serializable {
Expand Down Expand Up @@ -98,12 +106,29 @@ protected DotSamlResource(final SamlConfigurationService samlConfigura
* @param httpServletResponse {@link HttpServletResponse}
* @return Response
*/
@Operation(
summary = "Initiate SAML login",
description = "Initiates a SAML authentication request by redirecting the user to the Identity Provider (IDP) login screen. Requires IDP metadata to determine the SSO login endpoint."
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200",
description = "SAML authentication request initiated successfully (no body)"),
@ApiResponse(responseCode = "400",
description = "Bad request - invalid IDP configuration ID",
content = @Content(mediaType = "application/json")),
@ApiResponse(responseCode = "404",
description = "IDP configuration not found or not enabled",
content = @Content(mediaType = "application/json")),
@ApiResponse(responseCode = "500",
description = "Internal server error during SAML authentication initiation",
content = @Content(mediaType = "application/json"))
})
@GET
@Path( "/login/{idpConfigId}" )
@JSONP
@NoCache
@Produces( { MediaType.APPLICATION_JSON, "application/javascript" } )
public Response doLogin(@PathParam( "idpConfigId" ) final String idpConfigId,
@Produces( { MediaType.APPLICATION_JSON } )
public Response doLogin(@Parameter(description = "Identity Provider configuration ID (typically host ID)", required = true) @PathParam( "idpConfigId" ) final String idpConfigId,
@Context final HttpServletRequest httpServletRequest,
@Context final HttpServletResponse httpServletResponse) {

Expand Down Expand Up @@ -148,12 +173,36 @@ public Response doLogin(@PathParam( "idpConfigId" ) final String idpConfigId,
* @param httpServletResponse {@link HttpServletResponse}
* @throws IOException
*/
@Operation(
summary = "Process SAML login callback",
description = "Handles the callback from the Identity Provider after successful authentication. Extracts user information from the SAML assertion and creates/logs in the user to dotCMS.",
requestBody = @RequestBody(description = "SAML assertion data from Identity Provider", required = true,
content = {@Content(mediaType = "application/xml"),
@Content(mediaType = "application/x-www-form-urlencoded")})
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200",
description = "SAML login processed successfully - user logged in",
content = @Content(mediaType = "text/html")),
@ApiResponse(responseCode = "400",
description = "Bad request - invalid SAML assertion or missing data",
content = @Content(mediaType = "text/html")),
@ApiResponse(responseCode = "401",
description = "Unauthorized - SAML assertion validation failed",
content = @Content(mediaType = "text/html")),
@ApiResponse(responseCode = "404",
description = "IDP configuration not found or not enabled",
content = @Content(mediaType = "text/html")),
@ApiResponse(responseCode = "500",
description = "Internal server error during SAML login processing",
content = @Content(mediaType = "text/html"))
})
@POST
@Path("/login/{idpConfigId}")
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_FORM_URLENCODED})
@Produces( { MediaType.APPLICATION_XML, "text/html" } )
@NoCache
public void processLogin(@PathParam("idpConfigId") final String idpConfigId,
public void processLogin(@Parameter(description = "Identity Provider configuration ID (typically host ID)", required = true) @PathParam("idpConfigId") final String idpConfigId,
@Context final HttpServletRequest httpServletRequest,
@Context final HttpServletResponse httpServletResponse) throws IOException {

Expand Down Expand Up @@ -272,12 +321,33 @@ public void processLogin(@PathParam("idpConfigId") final String idpConfigId,
* @param httpServletResponse {@link HttpServletResponse}
* @throws IOException
*/
@Operation(
summary = "Get SAML metadata",
description = "Renders the XML metadata for the SAML Service Provider configuration. This endpoint is only accessible by administrators and provides the metadata required for IDP configuration."
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200",
description = "SAML metadata rendered successfully",
content = @Content(mediaType = "application/xml")),
@ApiResponse(responseCode = "401",
description = "Unauthorized - admin access required",
content = @Content(mediaType = "application/xml")),
@ApiResponse(responseCode = "403",
description = "Forbidden - user is not an administrator",
content = @Content(mediaType = "application/xml")),
@ApiResponse(responseCode = "404",
description = "IDP configuration not found or not enabled",
content = @Content(mediaType = "application/xml")),
@ApiResponse(responseCode = "500",
description = "Internal server error rendering metadata",
content = @Content(mediaType = "application/xml"))
})
@GET
@Path( "/metadata/{idpConfigId}" )
@JSONP
@NoCache
@Produces( { MediaType.APPLICATION_XML, "application/xml" } )
public void metadata( @PathParam( "idpConfigId" ) final String idpConfigId,
public void metadata( @Parameter(description = "Identity Provider configuration ID (typically host ID)", required = true) @PathParam( "idpConfigId" ) final String idpConfigId,
@Context final HttpServletRequest httpServletRequest,
@Context final HttpServletResponse httpServletResponse ) throws IOException {

Expand Down Expand Up @@ -313,12 +383,27 @@ public void metadata( @PathParam( "idpConfigId" ) final String idpConfigId,
throw new DoesNotExistException(message);
}

@Operation(
summary = "Process SAML logout (POST)",
description = "Processes a SAML logout request via POST method. Handles logout callbacks from the Identity Provider and redirects to the configured logout endpoint."
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200",
description = "SAML logout processed successfully",
content = @Content(mediaType = "text/html")),
@ApiResponse(responseCode = "404",
description = "IDP configuration not found or not enabled",
content = @Content(mediaType = "text/html")),
@ApiResponse(responseCode = "500",
description = "Internal server error during logout processing",
content = @Content(mediaType = "text/html"))
})
@POST
@Path("/logout/{idpConfigId}")
@NoCache
@Produces({MediaType.TEXT_HTML, MediaType.APPLICATION_XHTML_XML})
// Login configuration by id
public void logoutPost(@PathParam("idpConfigId") final String idpConfigId,
public void logoutPost(@Parameter(description = "Identity Provider configuration ID (typically host ID)", required = true) @PathParam("idpConfigId") final String idpConfigId,
@Context final HttpServletRequest httpServletRequest,
@Context final HttpServletResponse httpServletResponse) throws IOException, URISyntaxException {

Expand Down Expand Up @@ -350,12 +435,27 @@ public void logoutPost(@PathParam("idpConfigId") final String idpConfigId,
throw new DoesNotExistException(message);
}

@Operation(
summary = "Process SAML logout (GET)",
description = "Processes a SAML logout request via GET method. Initiates logout flow and redirects to the configured logout endpoint or builds a logout URL based on the request."
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200",
description = "SAML logout processed successfully",
content = @Content(mediaType = "text/html")),
@ApiResponse(responseCode = "404",
description = "IDP configuration not found or not enabled",
content = @Content(mediaType = "text/html")),
@ApiResponse(responseCode = "500",
description = "Internal server error during logout processing",
content = @Content(mediaType = "text/html"))
})
@GET
@Path("/logout/{idpConfigId}")
@NoCache
@Produces({MediaType.TEXT_HTML, MediaType.APPLICATION_XHTML_XML})
// Login configuration by id
public void logoutGet(@PathParam("idpConfigId") final String idpConfigId,
public void logoutGet(@Parameter(description = "Identity Provider configuration ID (typically host ID)", required = true) @PathParam("idpConfigId") final String idpConfigId,
@Context final HttpServletRequest httpServletRequest,
@Context final HttpServletResponse httpServletResponse) throws IOException, URISyntaxException {

Expand Down
74 changes: 71 additions & 3 deletions dotCMS/src/main/java/com/dotcms/rest/RoleResource.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.dotcms.rest;

import com.dotcms.rest.annotation.SwaggerCompliant;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
Expand All @@ -17,6 +18,12 @@
import com.dotmarketing.util.json.JSONArray;
import com.dotmarketing.util.json.JSONException;
import com.dotmarketing.util.json.JSONObject;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
Expand All @@ -25,6 +32,7 @@
import java.util.Map;


@SwaggerCompliant(value = "Core authentication and user management APIs", batch = 1)
@Tag(name = "Roles")
@Path("/role")
public class RoleResource {
Expand Down Expand Up @@ -61,10 +69,32 @@ public class RoleResource {
* @throws JSONException
*/

@Operation(
operationId = "loadRoleChildrenLegacy",
summary = "Load role children (deprecated)",
description = "Returns role hierarchy with first-level children for lazy-loading role tree in admin UI. If no ID provided, returns root roles. This endpoint is deprecated.",
deprecated = true
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200",
description = "Role children loaded successfully",
content = @Content(mediaType = "application/json",
schema = @Schema(type = "object", description = "Role hierarchy tree with child roles containing id, name, locked, and children properties"))),
@ApiResponse(responseCode = "401",
description = "Unauthorized - backend user authentication required",
content = @Content(mediaType = "application/json")),
@ApiResponse(responseCode = "403",
description = "Forbidden - insufficient permissions",
content = @Content(mediaType = "application/json")),
@ApiResponse(responseCode = "500",
description = "Internal server error",
content = @Content(mediaType = "application/json"))
})
@GET
@Path("/loadchildren/{params:.*}")
@Produces("application/json")
public Response loadChildren(@Context HttpServletRequest request, @Context final HttpServletResponse response, @PathParam("params") String params)
public Response loadChildren(@Context HttpServletRequest request, @Context final HttpServletResponse response,
@Parameter(description = "URL parameters including role ID (id=roleId or empty for root roles)", required = true) @PathParam("params") String params)
throws DotDataException, JSONException {

final InitDataObject initData = new WebResource.InitBuilder(webResource)
Expand Down Expand Up @@ -172,10 +202,29 @@ public Response loadChildren(@Context HttpServletRequest request, @Context final
* @throws JSONException
*/

@Operation(
operationId = "loadRoleByIdLegacy",
summary = "Load role by ID (deprecated)",
description = "Returns detailed role information including all role properties. Used for loading complete role details in admin UI. This endpoint is deprecated.",
deprecated = true
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200",
description = "Role loaded successfully",
content = @Content(mediaType = "application/json",
schema = @Schema(type = "object", description = "Role details including DBFQN, FQN, description, permissions, id, name, and other role properties"))),
@ApiResponse(responseCode = "401",
description = "Unauthorized - backend user authentication required",
content = @Content(mediaType = "application/json")),
@ApiResponse(responseCode = "500",
description = "Internal server error",
content = @Content(mediaType = "application/json"))
})
@GET
@Path("/loadbyid/{params:.*}")
@Produces("application/json")
public Response loadById(@Context HttpServletRequest request, @Context final HttpServletResponse response, @PathParam("params") String params) throws DotDataException, JSONException {
public Response loadById(@Context HttpServletRequest request, @Context final HttpServletResponse response,
@Parameter(description = "URL parameters including role ID (id=roleId)", required = true) @PathParam("params") String params) throws DotDataException, JSONException {

final InitDataObject initData = new WebResource.InitBuilder(webResource)
.requiredBackendUser(true)
Expand Down Expand Up @@ -241,11 +290,30 @@ public Response loadById(@Context HttpServletRequest request, @Context final Htt
* @throws DotDataException
* @throws JSONException
*/
@Operation(
operationId = "loadRolesByNameLegacy",
summary = "Load roles by name filter (deprecated)",
description = "Returns a filtered role tree structure where leaf nodes contain the specified name. Used for role filtering in admin UI. This endpoint is deprecated.",
deprecated = true
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200",
description = "Filtered roles loaded successfully",
content = @Content(mediaType = "application/json",
schema = @Schema(type = "object", description = "Filtered role tree structure with identifier, label, and items containing matching roles"))),
@ApiResponse(responseCode = "401",
description = "Unauthorized - backend user authentication required",
content = @Content(mediaType = "application/json")),
@ApiResponse(responseCode = "500",
description = "Internal server error",
content = @Content(mediaType = "application/json"))
})
@GET
@Path("/loadbyname/{params:.*}")
@Produces("application/json")
@SuppressWarnings("unchecked")
public Response loadByName(@Context HttpServletRequest request, @Context final HttpServletResponse response, @PathParam("params") String params) throws DotDataException, JSONException {
public Response loadByName(@Context HttpServletRequest request, @Context final HttpServletResponse response,
@Parameter(description = "URL parameters including name filter (name=filterText)", required = true) @PathParam("params") String params) throws DotDataException, JSONException {

final InitDataObject initData = new WebResource.InitBuilder(webResource)
.requiredBackendUser(true)
Expand Down
30 changes: 28 additions & 2 deletions dotCMS/src/main/java/com/dotcms/rest/UserResource.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.dotcms.rest;

import com.dotcms.rest.annotation.SwaggerCompliant;
import io.swagger.v3.oas.annotations.media.Schema;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
Expand All @@ -17,6 +19,11 @@
import com.liferay.portal.PortalException;
import com.liferay.portal.SystemException;
import com.liferay.portal.model.User;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;

import javax.servlet.http.HttpServletRequest;
Expand All @@ -29,6 +36,7 @@
* {@link com.dotcms.rest.api.v1.user.UserResource} end-point.
*/
@Deprecated
@SwaggerCompliant(value = "Core authentication and user management APIs", batch = 1)
@Tag(name = "Users")
@Path("/user")
public class UserResource {
Expand All @@ -43,12 +51,30 @@ public class UserResource {
* @throws JSONException
*
*/

@Operation(
operationId = "getLoggedInUserLegacy",
summary = "Get logged in user (deprecated)",
description = "Returns a JSON representation of the currently logged in user including userId, emailAddress, firstName, lastName, and roleId. This endpoint is deprecated - use v1 UserResource instead.",
deprecated = true
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200",
description = "User information retrieved successfully",
content = @Content(mediaType = "application/json",
schema = @Schema(type = "object", description = "User information containing userId, emailAddress, firstName, lastName, and roleId"))),
@ApiResponse(responseCode = "401",
description = "Unauthorized - backend user authentication required",
content = @Content(mediaType = "application/json")),
@ApiResponse(responseCode = "500",
description = "Internal server error",
content = @Content(mediaType = "application/json"))
})
@GET
@Path("/getloggedinuser/{params:.*}")
@Produces("application/json")
@Deprecated
public Response getLoggedInUser(@Context HttpServletRequest request, @Context final HttpServletResponse response, @PathParam("params") String params) throws DotDataException,
public Response getLoggedInUser(@Context HttpServletRequest request, @Context final HttpServletResponse response,
@Parameter(description = "URL parameters for the request", required = true) @PathParam("params") String params) throws DotDataException,
DotRuntimeException, PortalException, SystemException, JSONException {

final InitDataObject initData = new WebResource.InitBuilder(webResource)
Expand Down
Loading