Skip to content
Open
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
@@ -0,0 +1,34 @@
package teammates.common.datatransfer;

public enum OidcProviderNameType {
GOOGLE("Google"),
MS_ENTRA("MSEntra");

private final String providerName;

OidcProviderNameType(String providerName) {
this.providerName = providerName;
}

public String getProviderName() {
return providerName;
}

public String toString() {
return this.providerName;
}

public static OidcProviderNameType fromProviderName(String providerName) {
if (providerName == null) {
return null;
}
Comment on lines +22 to +24
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe can use StringHelper.isEmpty for null check


for (OidcProviderNameType provider : OidcProviderNameType.values()) {
if (provider.getProviderName().equals(providerName)) {
return provider;
}
}

return null;
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add newline at EOF

7 changes: 7 additions & 0 deletions src/main/java/teammates/common/util/Const.java
Copy link
Copy Markdown
Contributor

@iZUMi-kyouka iZUMi-kyouka Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issuer scheme is always HTTPS as per the spec. So what's the use of this constant

Original file line number Diff line number Diff line change
Expand Up @@ -402,4 +402,11 @@ public static class TaskQueue {
public static final String SEND_EMAIL_WORKER_URL = URI_PREFIX + "/sendEmail";
}

/**
* Constants related to OIDC
*/
public static class OIDC {
public static final String ISSUER_SCHEME = "https";
}

}
110 changes: 73 additions & 37 deletions src/main/java/teammates/common/util/FieldValidator.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package teammates.common.util;

import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
Expand Down Expand Up @@ -109,8 +111,10 @@ public final class FieldValidator {
public static final String SESSION_END_TIME_FIELD_NAME = "end time";
public static final String TIME_ZONE_FIELD_NAME = "time zone";

public static final String GOOGLE_ID_FIELD_NAME = "Google ID";
public static final int GOOGLE_ID_MAX_LENGTH = 254;
public static final String OIDC_ISSUER_FIELD_NAME = "OIDC issuer";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be a max length for issuer too?


public static final String OIDC_SUBJECT_FIELD_NAME = "OIDC subject";
public static final int OIDC_SUBJECT_MAX_LENGTH = 255;

public static final String ROLE_FIELD_NAME = "access-level";
public static final List<String> ROLE_ACCEPTED_VALUES =
Expand Down Expand Up @@ -196,13 +200,17 @@ public final class FieldValidator {
public static final String COURSE_ID_ERROR_MESSAGE_EMPTY_STRING =
EMPTY_STRING_ERROR_INFO + " " + HINT_FOR_CORRECT_COURSE_ID;

public static final String HINT_FOR_CORRECT_FORMAT_OF_GOOGLE_ID =
"A Google ID must be a valid id already registered with Google. "
+ HINT_FOR_CORRECT_FORMAT_FOR_SIZE_CAPPED_NON_EMPTY_NO_SPACES;
public static final String GOOGLE_ID_ERROR_MESSAGE =
ERROR_INFO + " " + HINT_FOR_CORRECT_FORMAT_OF_GOOGLE_ID;
public static final String GOOGLE_ID_ERROR_MESSAGE_EMPTY_STRING =
EMPTY_STRING_ERROR_INFO + " " + HINT_FOR_CORRECT_FORMAT_OF_GOOGLE_ID;
public static final String HINT_FOR_CORRECT_OIDC_ISSUER =
"An OIDC issuer must be a case-sensitive https URL with a host, may include a port and path, "
+ "and cannot contain query parameters or fragments.";
public static final String OIDC_ISSUER_ERROR_MESSAGE =
ERROR_INFO + " " + HINT_FOR_CORRECT_OIDC_ISSUER;

public static final String HINT_FOR_CORRECT_OIDC_SUBJECT =
"An OIDC subject must be a case-sensitive ASCII string. The value of a/an ${fieldName} should be "
+ "no longer than ${maxLength} characters.";
public static final String OIDC_SUBJECT_ERROR_MESSAGE =
ERROR_INFO + " " + HINT_FOR_CORRECT_OIDC_SUBJECT;

public static final String HINT_FOR_CORRECT_TIME_ZONE =
"The value must be one of the values from the time zone dropdown selector.";
Expand Down Expand Up @@ -263,11 +271,6 @@ public final class FieldValidator {
public static final String REGEX_EMAIL = "^[\\w+-][\\w+!#$%&'*/=?^_`{}~-]*+(\\.[\\w+!#$%&'*/=?^_`{}~-]+)*+"
+ "@([A-Za-z0-9-]+\\.)+[A-Za-z]+$";

/**
* Allows English alphabet, numbers, underscore, dot and hyphen.
*/
public static final String REGEX_GOOGLE_ID_NON_EMAIL = "[a-zA-Z0-9_.-]+";

private FieldValidator() {
// utility class
// Intentional private constructor to prevent instantiation.
Expand Down Expand Up @@ -315,30 +318,59 @@ public static String getInvalidityInfoForGracePeriod(Duration gracePeriod) {
}

/**
* Checks if {@code googleId} is not null, not empty, not longer than {@code GOOGLE_ID_MAX_LENGTH}, does
* not contain any invalid characters (| or %), AND is either a Google username (without the "@gmail.com")
* or a valid email address.
* @return An explanation of why the {@code googleId} is not acceptable.
* Returns an empty string if the {@code googleId} is acceptable.
* Checks if {@code issuer} is not null, not longer than {@code OIDC_ISSUER_MAX_LENGTH}, and is a
* valid OpenID Connect issuer identifier.
*
* <p>According to OpenID Connect Core 1.0, the issuer must be a case-sensitive URL using the https scheme that
* contains scheme, host, and optionally, port number and path components and no query or fragment components.
*
* @return An explanation of why the {@code issuer} is not acceptable.
* Returns an empty string if the {@code issuer} is acceptable.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong comment? "returns.."

*/
public static String getInvalidityInfoForGoogleId(String googleId) {

assert googleId != null;

boolean isValidFullEmail = isValidEmailAddress(googleId);
boolean isValidEmailWithoutDomain = StringHelper.isMatching(googleId, REGEX_GOOGLE_ID_NON_EMAIL);

if (googleId.isEmpty()) {
return getPopulatedEmptyStringErrorMessage(GOOGLE_ID_ERROR_MESSAGE_EMPTY_STRING,
GOOGLE_ID_FIELD_NAME, GOOGLE_ID_MAX_LENGTH);
} else if (isUntrimmed(googleId)) {
return WHITESPACE_ONLY_OR_EXTRA_WHITESPACE_ERROR_MESSAGE.replace("${fieldName}", GOOGLE_ID_FIELD_NAME);
} else if (googleId.length() > GOOGLE_ID_MAX_LENGTH) {
return getPopulatedErrorMessage(GOOGLE_ID_ERROR_MESSAGE, googleId, GOOGLE_ID_FIELD_NAME,
REASON_TOO_LONG, GOOGLE_ID_MAX_LENGTH);
} else if (!(isValidFullEmail || isValidEmailWithoutDomain)) {
return getPopulatedErrorMessage(GOOGLE_ID_ERROR_MESSAGE, googleId, GOOGLE_ID_FIELD_NAME,
REASON_INCORRECT_FORMAT, GOOGLE_ID_MAX_LENGTH);
public static String getInvalidityInfoForOidcIssuer(String issuer) {

assert issuer != null;

try {
URI issuerUri = new URI(issuer);
boolean isValid = Const.OIDC.ISSUER_SCHEME.equals(issuerUri.getScheme())
&& issuerUri.getHost() != null
&& issuerUri.getUserInfo() == null
&& issuerUri.getQuery() == null
&& issuerUri.getFragment() == null
&& issuerUri.isAbsolute()
&& !issuerUri.isOpaque();

if (!isValid) {
return getPopulatedErrorMessage(OIDC_ISSUER_ERROR_MESSAGE, issuer, OIDC_ISSUER_FIELD_NAME,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be simplified to reduce duplication?

REASON_INCORRECT_FORMAT);
}

} catch (URISyntaxException e) {
return getPopulatedErrorMessage(OIDC_ISSUER_ERROR_MESSAGE, issuer, OIDC_ISSUER_FIELD_NAME,
REASON_INCORRECT_FORMAT);
}

return "";
}

/**
* Checks if {@code subject} is not null, not longer than {@code OIDC_SUBJECT_MAX_LENGTH}, and
* contains only ASCII characters as required by OpenID Connect Core 1.0.
*
* @return An explanation of why the {@code subject} is not acceptable.
* Returns an empty string if the {@code subject} is acceptable.
*/
public static String getInvalidityInfoForOidcSubject(String subject) {

assert subject != null;

if (subject.length() > OIDC_SUBJECT_MAX_LENGTH) {
return getPopulatedErrorMessage(OIDC_SUBJECT_ERROR_MESSAGE, subject, OIDC_SUBJECT_FIELD_NAME,
REASON_TOO_LONG, OIDC_SUBJECT_MAX_LENGTH);
} else if (!isAsciiString(subject)) {
return getPopulatedErrorMessage(OIDC_SUBJECT_ERROR_MESSAGE, subject, OIDC_SUBJECT_FIELD_NAME,
REASON_INCORRECT_FORMAT, OIDC_SUBJECT_MAX_LENGTH);
}
return "";
}
Expand Down Expand Up @@ -998,6 +1030,10 @@ private static boolean isUntrimmed(String value) {
return value.length() != value.trim().length();
}

private static boolean isAsciiString(String value) {
return value.chars().allMatch(character -> character <= 0x7F);
}

/**
* Checks whether a given text input represents a format of a valid email address.
* @param email text input which needs the validation
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/teammates/common/util/HibernateUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import teammates.storage.sqlentity.FeedbackSession;
import teammates.storage.sqlentity.FeedbackSessionLog;
import teammates.storage.sqlentity.Instructor;
import teammates.storage.sqlentity.LoginIssuer;
import teammates.storage.sqlentity.Notification;
import teammates.storage.sqlentity.ReadNotification;
import teammates.storage.sqlentity.Section;
Expand Down Expand Up @@ -63,6 +64,7 @@ public final class HibernateUtil {
Course.class,
FeedbackSession.class,
Account.class,
LoginIssuer.class,
Notification.class,
ReadNotification.class,
User.class,
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/teammates/common/util/SanitizationHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,16 @@ private SanitizationHelper() {
}

/**
* Sanitizes a google ID by removing leading/trailing whitespace.
* Sanitizes a Login Identifier by removing leading/trailing whitespace.
*
* @return the sanitized google ID or null (if the parameter was null).
* @return the sanitized Login Identifier ID or null (if the parameter was null).
*/
public static String sanitizeGoogleId(String rawGoogleId) {
if (rawGoogleId == null) {
public static String sanitizeLoginIdentifier(String rawLoginIdentifier) {
if (rawLoginIdentifier == null) {
return null;
}

return rawGoogleId.trim();
return rawLoginIdentifier.trim();
}

/**
Expand Down
Loading