diff --git a/perun-oidc-server-webapp/src/main/resources/logback.xml b/perun-oidc-server-webapp/src/main/resources/logback.xml index 1e3ce72f09a0a1b4f050b7bf0da3ebf4de6c2ca6..aada621d62d4d62c7b25aff751f181aae1836f51 100644 --- a/perun-oidc-server-webapp/src/main/resources/logback.xml +++ b/perun-oidc-server-webapp/src/main/resources/logback.xml @@ -34,8 +34,16 @@ <suffixPattern>${PATTERN_SYSLOG}</suffixPattern> </appender> + <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> + <encoder> + <pattern>${PATTERN}</pattern> + <charset>UTF-8</charset> + </encoder> + </appender> + <root level="${log.level}"> <appender-ref ref="${log.to}"/> + <appender-ref ref="CONSOLE"/> <appender-ref ref="SENTRY"/> </root> diff --git a/perun-oidc-server/src/main/java/cz/muni/ics/oidc/server/claims/sources/VoBasedEdupersonScopedAffiliationsClaimSource.java b/perun-oidc-server/src/main/java/cz/muni/ics/oidc/server/claims/sources/VoBasedEdupersonScopedAffiliationsClaimSource.java new file mode 100644 index 0000000000000000000000000000000000000000..63ff439f43c799abe8a1b02bc8b0aed1d568f2ec --- /dev/null +++ b/perun-oidc-server/src/main/java/cz/muni/ics/oidc/server/claims/sources/VoBasedEdupersonScopedAffiliationsClaimSource.java @@ -0,0 +1,111 @@ +package cz.muni.ics.oidc.server.claims.sources; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; +import cz.muni.ics.oidc.server.claims.ClaimSource; +import cz.muni.ics.oidc.server.claims.ClaimSourceInitContext; +import cz.muni.ics.oidc.server.claims.ClaimSourceProduceContext; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Claim source for eduperson_scoped_affiliations MUNI. + * + * Configuration (replace [claimName] with the name of the claim): + * <ul> + * <li> + * <b>custom.claim.[claimName].source.config_file</b> - path to the YML config file, see + * 'eduperson_scoped_affiliations_mu_source.yml' for example configuration + * </li> + * </ul> + * + * @author Dominik Baránek <baranek@ics.muni.cz> + * @author Dominik Frantisek Bucik <bucik@ics.muni.cz> + */ +@Slf4j +public class EdupersonScopedAffiliationsMUSource extends ClaimSource { + + private static final String CONFIG_FILE = "config_file"; + private static final String KEY_SCOPE = "scope"; + private static final String KEY_VO_ID = "voId"; + private static final String KEY_AFFILIATIONS = "affiliations"; + private static final String KEY_VALUE = "value"; + private static final String KEY_GROUPS = "groups"; + + private static final String DEFAULT_PATH = "/etc/perun/eduperson_scoped_affiliations_mu_source.yml"; + + private final Map<List<Long>, String> affiliations = new HashMap<>(); + private Long voId = 363L; + private String valueScope = "muni.cz"; + + public EdupersonScopedAffiliationsMUSource(ClaimSourceInitContext ctx) { + super(ctx); + parseConfigFile(ctx.getProperty(CONFIG_FILE, DEFAULT_PATH)); + log.debug("{} - affiliations: '{}', voId: '{}', valueScope: '{}'", + getClaimName(), affiliations, voId, valueScope); + } + + @Override + public Set<String> getAttrIdentifiers() { + return Collections.emptySet(); + } + + @Override + public JsonNode produceValue(ClaimSourceProduceContext pctx) { + Long userId = pctx.getPerunUserId(); + ArrayNode result = JsonNodeFactory.instance.arrayNode(); + Set<Long> groups = pctx.getPerunAdapter().getUserGroupsIds(userId, voId); + for (Map.Entry<List<Long>, String> entry : affiliations.entrySet()) { + for (Long id: entry.getKey()) { + if (groups.contains(id)) { + String affiliation = entry.getValue() + '@' + valueScope; + log.trace("{} - added affiliation '{}' due to membership in group '{}'", + getClaimName(), affiliation, id); + result.add(affiliation); + break; + } + } + } + + if (result.size() == 0) { + String affiliation = "affiliate@" + valueScope; + log.trace("{} - user is not a member in any special groups, added default affiliation: '{}'", + getClaimName(), affiliation); + result.add(affiliation); + } + + log.debug("{} - produced value for user({}): '{}'", getClaimName(), userId, result); + return result; + } + + private void parseConfigFile(String file) { + log.trace("{} - Loading config file {}", getClaimName(), file); + YAMLMapper mapper = new YAMLMapper(); + try { + JsonNode root = mapper.readValue(new File(file), JsonNode.class); + valueScope = root.get(KEY_SCOPE).asText(); + voId = root.get(KEY_VO_ID).longValue(); + for (JsonNode affiliationMapping : root.path(KEY_AFFILIATIONS)) { + String value = affiliationMapping.path(KEY_VALUE).asText(); + List<Long> gids = new ArrayList<>(); + for (JsonNode gid : affiliationMapping.path(KEY_GROUPS)) { + gids.add(gid.asLong()); + } + affiliations.put(gids, value); + } + } catch (IOException ex) { + log.warn("{} - cannot read claim configuration file: '{}'", getClaimName(), file); + } + } + +}