Skip to content
Closed
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
627 changes: 626 additions & 1 deletion CLAUDE.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,7 @@ describe('DotContentTypesEditComponent', () => {
contentTypeForm = de.query(By.css('dot-content-types-form'));
});

it('should udpate content type', () => {
it('should update content type', () => {
const responseContentType = Object.assign({}, fakeContentType, {
fields: [{ hello: 'world' }]
});
Expand Down
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,28 @@
import com.dotcms.rest.ResponseEntityView;
import com.dotcms.rest.WebResource;
import com.dotcms.rest.annotation.NoCache;
import com.dotcms.rest.annotation.SwaggerCompliant;
import com.google.common.collect.ImmutableList;
import com.liferay.portal.model.User;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import io.swagger.v3.oas.annotations.Operation;
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 static com.dotcms.util.CollectionsUtils.toImmutableList;

/**
* This end-point provides access to information associated to dotCMS FieldType.
*/
@SwaggerCompliant(value = "Content management and workflow APIs", batch = 2)
@Path("/v1/fieldTypes")
@Tag(name = "Content Type Field", description = "Content type field definitions and configuration")
@Tag(name = "Content Type Field")
public class FieldTypeResource {

private final WebResource webResource;
Expand All @@ -43,10 +50,23 @@ public FieldTypeResource(final WebResource webresource, FieldTypeAPI fieldTypeAP
this.fieldTypeAPI = fieldTypeAPI;
}

@Operation(
summary = "Get field types",
description = "Retrieves all available field types in dotCMS for content type configuration"
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200",
description = "Field types retrieved successfully",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = ResponseEntityFieldTypeListView.class))),
@ApiResponse(responseCode = "401",
description = "Unauthorized - authentication required",
content = @Content(mediaType = "application/json"))
})
@GET
@JSONP
@NoCache
@Produces({ MediaType.APPLICATION_JSON, "application/javascript" })
@Produces({ MediaType.APPLICATION_JSON })
public Response getFieldTypes(@Context final HttpServletRequest req) {

final InitDataObject initData = this.webResource.init(null, true, req, true, null);
Expand All @@ -56,6 +76,6 @@ public Response getFieldTypes(@Context final HttpServletRequest req) {
.map(FieldType::toMap)
.collect(toImmutableList());

return Response.ok( new ResponseEntityView<List<Map<String, Object>>>( fieldTypesMap ) ).build();
return Response.ok( new ResponseEntityFieldTypeListView( fieldTypesMap ) ).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.dotcms.contenttype.model.field;

import com.dotcms.rest.ResponseEntityView;
import java.util.List;
import java.util.Map;

/**
* Entity View for field type collection responses.
* Contains list of field type configurations.
*
* @author Steve Bolton
*/
public class ResponseEntityFieldTypeListView extends ResponseEntityView<List<Map<String, Object>>> {
public ResponseEntityFieldTypeListView(final List<Map<String, Object>> entity) {
super(entity);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,46 @@
import com.dotcms.publisher.business.DotPublisherException;
import com.dotcms.publisher.business.PublishAuditAPI;
import com.dotcms.publisher.business.PublishAuditStatus;
import com.dotcms.rest.annotation.SwaggerCompliant;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.dotmarketing.util.Logger;
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;

@SwaggerCompliant(value = "Publishing and content distribution APIs", batch = 5)
@Path("/auditPublishing")
@Tag(name = "Publishing")
public class AuditPublishingResource {
private PublishAuditAPI auditAPI = PublishAuditAPI.getInstance();

@Operation(
summary = "Get publish audit status",
description = "Retrieves the publishing audit status for a specific bundle by its ID. Returns the serialized status information in XML format."
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200",
description = "Publish audit status retrieved successfully",
content = @Content(mediaType = "text/xml")),
@ApiResponse(responseCode = "404",
description = "Bundle not found or no audit status available",
content = @Content(mediaType = "text/xml")),
@ApiResponse(responseCode = "500",
description = "Internal server error retrieving audit status",
content = @Content(mediaType = "text/xml"))
})
@GET
@Path("/get/{bundleId:.*}")
@Produces(MediaType.TEXT_XML)
public Response get(@PathParam("bundleId") String bundleId) {
public Response get(@Parameter(description = "Bundle ID to get audit status for", required = true) @PathParam("bundleId") String bundleId) {
PublishAuditStatus status = null;

try {
Expand Down
Loading
Loading