Skip to content

Instantly share code, notes, and snippets.

@ilgrosso
Created October 29, 2024 14:00
Show Gist options
  • Save ilgrosso/db3933b55579fa43b4611d093941974f to your computer and use it in GitHub Desktop.
Save ilgrosso/db3933b55579fa43b4611d093941974f to your computer and use it in GitHub Desktop.
OpenFGARegisteredServiceAccessStrategy for Syncope 3.0 WA
package org.apache.syncope.wa.starter.services;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.micrometer.core.instrument.util.IOUtils;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apereo.cas.configuration.support.ExpressionLanguageCapable;
import org.apereo.cas.services.BaseRegisteredServiceAccessStrategy;
import org.apereo.cas.util.HttpUtils;
import org.apereo.cas.util.HttpUtils.HttpExecutionRequest;
import org.apereo.cas.util.LoggingUtils;
import org.apereo.cas.util.function.FunctionUtils;
import org.apereo.cas.util.serialization.JacksonObjectMapperFactory;
import org.apereo.cas.util.spring.SpringExpressionLanguageValueResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@ToString(callSuper = true)
@Getter
@Setter
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
@Accessors(chain = true)
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
@Slf4j
public class OpenFGARegisteredServiceAccessStrategy extends BaseRegisteredServiceAccessStrategy {
private static final long serialVersionUID = -1108201604115278440L;
private static final Logger LOG = LoggerFactory.getLogger(OpenFGARegisteredServiceAccessStrategy.class);
private static final ObjectMapper MAPPER = JacksonObjectMapperFactory.builder().
defaultTypingEnabled(false).build().toObjectMapper();
private String relation;
private String object;
@ExpressionLanguageCapable
private String apiUrl;
@ExpressionLanguageCapable
private String storeId;
@ExpressionLanguageCapable
private String token;
@SuppressWarnings("unchecked")
@Override
public boolean doPrincipalAttributesAllowServiceAccess(
final String principal,
final Map<String, Object> principalAttributes) {
HttpResponse response = null;
try {
Map<String, String> headers = new HashMap<>();
headers.put(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
if (StringUtils.isNotBlank(token)) {
headers.put(
HttpHeaders.AUTHORIZATION,
"Bearer " + SpringExpressionLanguageValueResolver.getInstance().resolve(this.token));
}
String url = StringUtils.removeEnd(
SpringExpressionLanguageValueResolver.getInstance().resolve(this.apiUrl), "/");
String store = StringUtils.removeEnd(
SpringExpressionLanguageValueResolver.getInstance().resolve(this.storeId), "/");
String fgaApiUrl = String.format("%s/stores/%s/check", url, store);
String checkEntity = AuthorizationRequestEntity.builder()
.object(this.object)
.relation(StringUtils.defaultIfBlank(this.relation, "owner"))
.user(principal)
.build()
.toJson();
HttpExecutionRequest exec = HttpExecutionRequest.builder()
.method(HttpMethod.POST)
.url(fgaApiUrl)
.headers(headers)
.entity(checkEntity)
.build();
LOG.debug("Submitting authorization request to [{}] for [{}]", fgaApiUrl, checkEntity);
response = HttpUtils.execute(exec);
if (HttpStatus.resolve(response.getStatusLine().getStatusCode()).is2xxSuccessful()) {
try (InputStream content = response.getEntity().getContent()) {
String results = IOUtils.toString(content, StandardCharsets.UTF_8);
LOG.trace("Received response from endpoint [{}] as [{}]", url, results);
Map<String, Object> payload = MAPPER.readValue(
results, new TypeReference<HashMap<String, Object>>() {
});
return (Boolean) payload.getOrDefault("allowed", Boolean.FALSE);
}
}
} catch (final Exception e) {
LoggingUtils.error(LOG, e);
} finally {
HttpUtils.close(response);
}
return false;
}
@SuperBuilder
@Getter
private static final class AuthorizationRequestEntity {
private final String user;
private final String relation;
private final String object;
@JsonIgnore
public String toJson() {
return FunctionUtils.doUnchecked(() -> MAPPER.writeValueAsString(Map.of("tuple_key", this)));
}
}
}
@ilgrosso
Copy link
Author

Save it under wa/starter/src/main/java/org/apache/syncope/wa/starter/services/OpenFGARegisteredServiceAccessStrategy.java

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment