Skip to content

Commit

Permalink
Development: Fix an issue with LTI authorization types (#10098)
Browse files Browse the repository at this point in the history
  • Loading branch information
krusche authored Jan 2, 2025
1 parent 7d59fe0 commit 82bf67c
Show file tree
Hide file tree
Showing 18 changed files with 118 additions and 282 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,11 @@ else if (channel.getExam() != null) {
}
}

/**
* Converts the DTO to a channel entity
*
* @return the created channel entity based on the attributes in the DTO
*/
public Channel toChannel() {
Channel channel = new Channel();
channel.setName(this.name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import de.tum.cit.aet.artemis.core.exception.LtiEmailAlreadyInUseException;
import de.tum.cit.aet.artemis.core.security.SecurityUtils;
import de.tum.cit.aet.artemis.lti.dto.Claims;
import de.tum.cit.aet.artemis.lti.dto.LtiAuthenticationResponse;
import de.tum.cit.aet.artemis.lti.dto.Lti13AuthenticationResponse;
import de.tum.cit.aet.artemis.lti.service.Lti13Service;
import uk.ac.ox.ctl.lti13.security.oauth2.client.lti.authentication.OidcAuthenticationToken;
import uk.ac.ox.ctl.lti13.security.oauth2.client.lti.web.OAuth2LoginAuthenticationFilter;
Expand Down Expand Up @@ -125,7 +125,7 @@ private void writeResponse(String targetLinkUri, OidcIdToken ltiIdToken, String
log.info("User is authenticated, building LTI response");
lti13Service.buildLtiResponse(uriBuilder, response);
}
LtiAuthenticationResponse jsonResponse = new LtiAuthenticationResponse(uriBuilder.build().toUriString(), ltiIdToken.getTokenValue(), clientRegistrationId);
Lti13AuthenticationResponse jsonResponse = new Lti13AuthenticationResponse(uriBuilder.build().toUriString(), ltiIdToken.getTokenValue(), clientRegistrationId);

response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/de/tum/cit/aet/artemis/lti/dto/Claims.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package de.tum.cit.aet.artemis.lti.dto;

public class Claims extends uk.ac.ox.ctl.lti13.lti.Claims {
public final class Claims extends uk.ac.ox.ctl.lti13.lti.Claims {

/**
* Constant for LTI Assignment and Grade Services (AGS) claim endpoint.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@

import org.springframework.security.oauth2.core.oidc.OidcIdToken;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
* A wrapper record for an LTI 1.3 Assignment and Grading Services Claim. We support the Score Publishing Service in order to transmit scores.
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public record Lti13AgsClaim(List<String> scope, String lineItem) {

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package de.tum.cit.aet.artemis.lti.dto;

import com.fasterxml.jackson.annotation.JsonInclude;

/**
* Holds LTI authentication response details.
*
* @param targetLinkUri URI targeted in the LTI process.
* @param ltiIdToken LTI service provided ID token.
* @param clientRegistrationId Client's registration ID with LTI service.
*/
public record LtiAuthenticationResponse(String targetLinkUri, String ltiIdToken, String clientRegistrationId) {
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public record Lti13AuthenticationResponse(String targetLinkUri, String ltiIdToken, String clientRegistrationId) {
}
Original file line number Diff line number Diff line change
@@ -1,276 +1,20 @@
package de.tum.cit.aet.artemis.lti.dto;

import java.util.Arrays;
import java.util.List;

import org.springframework.security.oauth2.core.AuthorizationGrantType;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

import de.tum.cit.aet.artemis.lti.config.CustomLti13Configurer;

/**
* Represents the client registration details for an LTI 1.3 integration.
* This class encapsulates information required for LTI 1.3 client registration,
* including response types, grant types, redirect URIs, and tool configuration.
*/
public class Lti13ClientRegistration {

@JsonProperty("client_id")
private String clientId;

@JsonProperty("response_types")
private List<String> responseTypes;

@JsonProperty("grant_types")
private List<String> grantTypes;

@JsonProperty("initiate_login_uri")
private String initiateLoginUri;

@JsonProperty("redirect_uris")
private List<String> redirectUris;

@JsonProperty("client_name")
private String clientName;

@JsonProperty("jwks_uri")
private String jwksUri;

@JsonProperty("logo_uri")
private String logoUri;

@JsonProperty("token_endpoint_auth_method")
private String tokenEndpointAuthMethod;

private String scope;

@JsonProperty("https://purl.imsglobal.org/spec/lti-tool-configuration")
private Lti13ToolConfiguration lti13ToolConfiguration;

/**
* Default constructor necessary for conversion.
*/
public Lti13ClientRegistration() { // Necessary for conversion
}

/**
* Constructs a new Lti13ClientRegistration with specified server URL and client registration ID.
* Initializes various properties such as grant types, response types, and tool configurations.
*
* @param serverUrl The server URL for LTI configuration.
* @param clientRegistrationId The client registration ID for LTI configuration.
*/
public Lti13ClientRegistration(String serverUrl, String clientRegistrationId) {
this.setGrantTypes(Arrays.asList(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue(), AuthorizationGrantType.AUTHORIZATION_CODE.getValue()));
this.setResponseTypes(List.of("id_token"));
this.setClientName("Artemis - " + serverUrl);
this.setTokenEndpointAuthMethod("private_key_jwt");
this.setScope(String.join(" ", List.of(Scopes.AGS_SCORE, Scopes.AGS_RESULT)));
this.setRedirectUris(List.of(serverUrl + "/" + CustomLti13Configurer.LTI13_LOGIN_REDIRECT_PROXY_PATH));
this.setInitiateLoginUri(serverUrl + "/" + CustomLti13Configurer.LTI13_LOGIN_INITIATION_PATH + "/" + clientRegistrationId);
this.setJwksUri(serverUrl + "/.well-known/jwks.json");
this.setLogoUri(serverUrl + "/public/images/logo.png");

Lti13ToolConfiguration toolConfiguration = getLti13ToolConfiguration(serverUrl);
this.setLti13ToolConfiguration(toolConfiguration);
}

private static Lti13ToolConfiguration getLti13ToolConfiguration(String serverUrl) {
Lti13ToolConfiguration toolConfiguration = new Lti13ToolConfiguration();

// Extracting the domain from the server URL
String[] urlParts = serverUrl.split("://");
String domain = "";
if (urlParts.length >= 1) {
domain = urlParts[1]; // Domain cannot include protocol
}
toolConfiguration.setDomain(domain);
toolConfiguration.setTargetLinkUri(serverUrl + "/courses");
toolConfiguration.setDescription("Artemis: Interactive Learning with Individual Feedback");
toolConfiguration.setClaims(Arrays.asList("iss", "email", "sub", "name", "given_name", "family_name"));
Message deepLinkingMessage = new Message(CustomLti13Configurer.LTI13_DEEPLINK_MESSAGE_REQUEST, serverUrl + "/" + CustomLti13Configurer.LTI13_DEEPLINK_REDIRECT_PATH);
toolConfiguration.setMessages(List.of(deepLinkingMessage));
return toolConfiguration;
}

public String getClientId() {
return clientId;
}

public void setClientId(String clientId) {
this.clientId = clientId;
}

public List<String> getResponseTypes() {
return responseTypes;
}

public void setResponseTypes(List<String> responseTypes) {
this.responseTypes = responseTypes;
}

public List<String> getGrantTypes() {
return grantTypes;
}

public void setGrantTypes(List<String> grantTypes) {
this.grantTypes = grantTypes;
}

public String getInitiateLoginUri() {
return initiateLoginUri;
}

public void setInitiateLoginUri(String initiateLoginUri) {
this.initiateLoginUri = initiateLoginUri;
}

public List<String> getRedirectUris() {
return redirectUris;
}

public void setRedirectUris(List<String> redirectUris) {
this.redirectUris = redirectUris;
}

public String getClientName() {
return clientName;
}

public void setClientName(String clientName) {
this.clientName = clientName;
}

public String getJwksUri() {
return jwksUri;
}

public void setJwksUri(String jwksUri) {
this.jwksUri = jwksUri;
}

public String getLogoUri() {
return logoUri;
}

public void setLogoUri(String logoUri) {
this.logoUri = logoUri;
}

public String getTokenEndpointAuthMethod() {
return tokenEndpointAuthMethod;
}

public void setTokenEndpointAuthMethod(String tokenEndpointAuthMethod) {
this.tokenEndpointAuthMethod = tokenEndpointAuthMethod;
}

public String getScope() {
return scope;
}

public void setScope(String scope) {
this.scope = scope;
}

public Lti13ToolConfiguration getLti13ToolConfiguration() {
return lti13ToolConfiguration;
}

public void setLti13ToolConfiguration(Lti13ToolConfiguration lti13ToolConfiguration) {
this.lti13ToolConfiguration = lti13ToolConfiguration;
}

/**
* Inner class representing the LTI 1.3 tool configuration.
*/
public static class Lti13ToolConfiguration {

private String domain;

@JsonProperty("target_link_uri")
private String targetLinkUri;

private String description;

private List<Message> messages;

private List<String> claims;

public String getDomain() {
return domain;
}

public void setDomain(String domain) {
this.domain = domain;
}

public String getTargetLinkUri() {
return targetLinkUri;
}

public void setTargetLinkUri(String targetLinkUri) {
this.targetLinkUri = targetLinkUri;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public List<Message> getMessages() {
return messages;
}

public void setMessages(List<Message> messages) {
this.messages = messages;
}

public List<String> getClaims() {
return claims;
}

public void setClaims(List<String> claims) {
this.claims = claims;
}
}

/**
* Inner class representing a message in LTI 1.3 tool configuration.
*/
public static class Message {

private String type;

@JsonProperty("target_link_uri")
private String targetLinkUri;

public Message() {// Necessary for conversion
}

public Message(String type, String targetLinkUri) {
this.type = type;
this.targetLinkUri = targetLinkUri;
}

public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}

public String getTargetLinkUri() {
return targetLinkUri;
}
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public record Lti13ClientRegistration(@JsonProperty("client_id") String clientId, @JsonProperty("response_types") List<String> responseTypes,
@JsonProperty("grant_types") List<String> grantTypes, @JsonProperty("initiate_login_uri") String initiateLoginUri, @JsonProperty("redirect_uris") List<String> redirectUris,
@JsonProperty("client_name") String clientName, @JsonProperty("jwks_uri") String jwksUri, @JsonProperty("logo_uri") String logoUri,
@JsonProperty("token_endpoint_auth_method") String tokenEndpointAuthMethod, String scope,
@JsonProperty("https://purl.imsglobal.org/spec/lti-tool-configuration") Lti13ToolConfiguration lti13ToolConfiguration) {

public void setTargetLinkUri(String targetLinkUri) {
this.targetLinkUri = targetLinkUri;
}
}
}
Loading

0 comments on commit 82bf67c

Please sign in to comment.