Skip to content

Commit

Permalink
Merge branch 'develop' into fix-delete-feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisknedl authored Dec 5, 2023
2 parents c0b801e + 7101235 commit 0d736f2
Show file tree
Hide file tree
Showing 95 changed files with 3,578 additions and 1,068 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ plugins {
}

group = "de.tum.in.www1.artemis"
version = "6.7.0"
version = "6.7.1"
description = "Interactive Learning with Individual Feedback"

java {
Expand Down
1 change: 1 addition & 0 deletions docs/admin/setup.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ For information on how to set up extension services to activate additional funct
:includehidden:
:maxdepth: 2

setup/security
setup/programming-exercises
setup/customization
setup/legal-documents
Expand Down
56 changes: 56 additions & 0 deletions docs/admin/setup/security.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
Security
========


Passwords
---------

The Artemis configuration files contain a few default passwords and secrets
that have to be overridden in your own configuration files or via environment
variables (`Spring relaxed binding <https://github.com/spring-projects/spring-boot/wiki/Relaxed-Binding-2.0>`_).

.. code-block:: yaml
artemis:
user-management:
internal-admin:
username: "artemis-admin"
# can be changed later, Artemis will update the password in the database
# and connected systems on the next start
password: "artemis-admin"
jhipster:
security:
authentication:
jwt:
# used to sign the JWT tokens for user authentication
# can be changed later, will require all users to log in again
#
# encoded using Base64 (you can use `echo 'secret-key'|base64` on your command line)
base64-secret: ""
registry:
password: "change-me" # only for distributed setups with multiple Artemis instances
spring:
prometheus:
# if Prometheus monitoring is enabled: a comma-separated list of
# IPs that are allowed to access the metrics endpoint
monitoring-ip: "127.0.0.1"
websocket:
broker:
username: "guest" # only for distributed setups
password: "guest" # only for distributed setups
.. note::

The usernames/passwords for external systems (Bamboo, Bitbucket, GitLab,
Jenkins, …) are not listed here, since the general setup documentation
describes how to set up those systems.
Without replacing the default values, the connection to them will not work.


.. note::

Ensure restrictive access to the configuration files, so that access is only
possible for the system account that runs Artemis and administrators.

4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "artemis",
"version": "6.7.0",
"version": "6.7.1",
"description": "Interactive Learning with Individual Feedback",
"private": true,
"license": "MIT",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,27 @@
@Profile("lti")
public class CustomLti13Configurer extends Lti13Configurer {

/** Path for login. **/
private static final String LOGIN_PATH = "/auth-login";

/** Path for initiating login process. */
private static final String LOGIN_INITIATION_PATH = "/initiate-login";

/** Base path for LTI 1.3 API endpoints. */
public static final String LTI13_BASE_PATH = "/api/public/lti13";

/** Full path for LTI 1.3 login. */
public static final String LTI13_LOGIN_PATH = LTI13_BASE_PATH + LOGIN_PATH;

/** Full path for LTI 1.3 login initiation. */
public static final String LTI13_LOGIN_INITIATION_PATH = LTI13_BASE_PATH + LOGIN_INITIATION_PATH;

/** Redirect proxy path for LTI 1.3 login. */
public static final String LTI13_LOGIN_REDIRECT_PROXY_PATH = LTI13_BASE_PATH + "/auth-callback";

/** Path for LTI 1.3 deep linking. */
public static final String LTI13_DEEPLINKING_PATH = "/lti/deep-linking/";

public CustomLti13Configurer() {
super.ltiPath(LTI13_BASE_PATH);
super.loginInitiationPath(LOGIN_INITIATION_PATH);
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/de/tum/in/www1/artemis/domain/lti/Claims.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,15 @@

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

/**
* Constant for LTI Assignment and Grade Services (AGS) claim endpoint.
* Used to identify the AGS service endpoint in LTI messages.
*/
public static final String AGS_CLAIM = "https://purl.imsglobal.org/spec/lti-ags/claim/endpoint";

/**
* Constant for LTI Deep Linking message claim.
* Used to carry messages specific to LTI Deep Linking requests and responses.
*/
public static final String MSG = "https://purl.imsglobal.org/spec/lti-dl/claim/msg";
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public Lti13ClientRegistration(String serverUrl, Course course, String clientReg
toolConfiguration.setDomain(domain);
toolConfiguration.setTargetLinkUri(serverUrl + "/courses/" + course.getId());
toolConfiguration.setClaims(Arrays.asList("iss", "email", "sub", "name", "given_name", "family_name"));
Message deepLinkingMessage = new Message("LtiDeepLinkingRequest", serverUrl + CustomLti13Configurer.LTI13_BASE_PATH + "/deep-linking/" + course.getId());
Message deepLinkingMessage = new Message("LtiDeepLinkingRequest", serverUrl + CustomLti13Configurer.LTI13_DEEPLINKING_PATH + course.getId());
toolConfiguration.setMessages(List.of(deepLinkingMessage));
this.setLti13ToolConfiguration(toolConfiguration);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
package de.tum.in.www1.artemis.domain.lti;

import java.util.HashMap;
import java.util.Map;

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

import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

/**
* Represents the LTI 1.3 Deep Linking Response.
* It encapsulates the necessary information to construct a valid deep linking response
* according to the LTI 1.3 specification.
*/
public class Lti13DeepLinkingResponse {

@JsonProperty("aud")
private String aud;

@JsonProperty("iss")
private String iss;

@JsonProperty("exp")
private String exp;

@JsonProperty("iat")
private String iat;

@JsonProperty("nonce")
private String nonce;

@JsonProperty(Claims.MSG)
private String message;

@JsonProperty(Claims.LTI_DEPLOYMENT_ID)
private String deploymentId;

@JsonProperty(Claims.MESSAGE_TYPE)
private String messageType;

@JsonProperty(Claims.LTI_VERSION)
private String ltiVersion;

@JsonProperty(Claims.CONTENT_ITEMS)
private String contentItems;

private JsonObject deepLinkingSettings;

private String clientRegistrationId;

private String returnUrl;

/**
* Constructs an Lti13DeepLinkingResponse from an OIDC ID token and client registration ID.
*
* @param ltiIdToken the OIDC ID token
* @param clientRegistrationId the client registration ID
*/
public Lti13DeepLinkingResponse(OidcIdToken ltiIdToken, String clientRegistrationId) {
validateClaims(ltiIdToken);

this.deepLinkingSettings = JsonParser.parseString(ltiIdToken.getClaim(Claims.DEEP_LINKING_SETTINGS).toString()).getAsJsonObject();
this.setReturnUrl(this.deepLinkingSettings.get("deep_link_return_url").getAsString());
this.clientRegistrationId = clientRegistrationId;

this.setAud(ltiIdToken.getClaim("iss").toString());
this.setIss(ltiIdToken.getClaim("aud").toString().replace("[", "").replace("]", ""));
this.setExp(ltiIdToken.getClaim("exp").toString());
this.setIat(ltiIdToken.getClaim("iat").toString());
this.setNonce(ltiIdToken.getClaim("nonce").toString());
this.setMessage("Content successfully linked");
this.setDeploymentId(ltiIdToken.getClaim(Claims.LTI_DEPLOYMENT_ID).toString());
this.setMessageType("LtiDeepLinkingResponse");
this.setLtiVersion("1.3.0");
}

/**
* Retrieves a map of claims to be included in the ID token.
*
* @return a map of claims
*/
public Map<String, Object> getClaims() {
Map<String, Object> claims = new HashMap<>();

claims.put("aud", aud);
claims.put("iss", iss);
claims.put("exp", exp);
claims.put("iat", iat);
claims.put("nonce", nonce);
claims.put(Claims.MSG, message);
claims.put(Claims.LTI_DEPLOYMENT_ID, deploymentId);
claims.put(Claims.MESSAGE_TYPE, messageType);
claims.put(Claims.LTI_VERSION, ltiVersion);
claims.put(Claims.CONTENT_ITEMS, contentItems);

return claims;
}

private void validateClaims(OidcIdToken ltiIdToken) {
if (ltiIdToken == null) {
throw new IllegalArgumentException("The OIDC ID token must not be null.");
}

Object deepLinkingSettingsElement = ltiIdToken.getClaim(Claims.DEEP_LINKING_SETTINGS);
if (deepLinkingSettingsElement == null) {
throw new IllegalArgumentException("Missing or invalid deep linking settings in ID token.");
}

ensureClaimPresent(ltiIdToken, "iss");
ensureClaimPresent(ltiIdToken, "aud");
ensureClaimPresent(ltiIdToken, "exp");
ensureClaimPresent(ltiIdToken, "iat");
ensureClaimPresent(ltiIdToken, "nonce");
ensureClaimPresent(ltiIdToken, Claims.LTI_DEPLOYMENT_ID);
}

private void ensureClaimPresent(OidcIdToken ltiIdToken, String claimName) {
Object claimValue = ltiIdToken.getClaim(claimName);
if (claimValue == null) {
throw new IllegalArgumentException("Missing claim: " + claimName);
}
}

public void setAud(String aud) {
this.aud = aud;
}

public String getIss() {
return iss;
}

public void setIss(String iss) {
this.iss = iss;
}

public String getExp() {
return exp;
}

public void setExp(String exp) {
this.exp = exp;
}

public String getIat() {
return iat;
}

public void setIat(String iat) {
this.iat = iat;
}

public String getNonce() {
return nonce;
}

public void setNonce(String nonce) {
this.nonce = nonce;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public String getDeploymentId() {
return deploymentId;
}

public void setDeploymentId(String deploymentId) {
this.deploymentId = deploymentId;
}

public String getMessageType() {
return messageType;
}

public void setMessageType(String messageType) {
this.messageType = messageType;
}

public String getLtiVersion() {
return ltiVersion;
}

public void setLtiVersion(String ltiVersion) {
this.ltiVersion = ltiVersion;
}

public String getContentItems() {
return contentItems;
}

public void setContentItems(String contentItems) {
this.contentItems = contentItems;
}

public JsonObject getDeepLinkingSettings() {
return deepLinkingSettings;
}

public void setDeepLinkingSettings(JsonObject deepLinkingSettings) {
this.deepLinkingSettings = deepLinkingSettings;
}

public String getClientRegistrationId() {
return clientRegistrationId;
}

public void setClientRegistrationId(String clientRegistrationId) {
this.clientRegistrationId = clientRegistrationId;
}

public String getAud() {
return aud;
}

public String getReturnUrl() {
return returnUrl;
}

public void setReturnUrl(String returnUrl) {
this.returnUrl = returnUrl;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package de.tum.in.www1.artemis.domain.lti;

/**
* 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 LtiAuthenticationResponseDTO(String targetLinkUri, String ltiIdToken, String clientRegistrationId) {
}
Loading

0 comments on commit 0d736f2

Please sign in to comment.