Skip to content

Commit

Permalink
Implement missing user sec- headers, and JSON header contributor for …
Browse files Browse the repository at this point in the history
…user and org
  • Loading branch information
groldan committed Apr 18, 2022
1 parent 56590fa commit 671f07b
Show file tree
Hide file tree
Showing 25 changed files with 1,406 additions and 277 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import org.georchestra.gateway.filter.headers.providers.GeorchestraOrganizationHeadersContributor;
import org.georchestra.gateway.filter.headers.providers.GeorchestraUserHeadersContributor;
import org.georchestra.gateway.filter.headers.providers.JsonPayloadHeadersContributor;
import org.georchestra.gateway.filter.headers.providers.SecProxyHeaderContributor;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.context.ApplicationContext;
Expand Down Expand Up @@ -58,6 +59,10 @@ public class HeaderFiltersConfiguration {
return new GeorchestraOrganizationHeadersContributor();
}

public @Bean JsonPayloadHeadersContributor jsonPayloadHeadersContributor() {
return new JsonPayloadHeadersContributor();
}

/**
* General purpose {@link GatewayFilterFactory} to remove incoming HTTP request
* headers based on a Java regular expression
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class GeorchestraOrganizationHeadersContributor extends HeaderContributor
.ifPresent(mappings -> {
Optional<Organization> org = GeorchestraOrganizations.resolve(exchange);
add(headers, "sec-orgname", mappings.getOrgname(), org.map(Organization::getName));
add(headers, "sec-org-id", mappings.getOrgid(), org.map(Organization::getId));
add(headers, "sec-orgid", mappings.getOrgid(), org.map(Organization::getId));
add(headers, "sec-org-lastupdated", mappings.getOrgid(), org.map(Organization::getLastUpdated));
});
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,39 +33,36 @@
/**
* Contributes user-related {@literal sec-*} request headers.
*
* <p>
* For any
*
* @see GeorchestraUsers#resolve
* @see GeorchestraTargetConfig
*/
public class GeorchestraUserHeadersContributor extends HeaderContributor {

public @Override Consumer<HttpHeaders> prepare(ServerWebExchange exchange) {
return headers -> {
GeorchestraTargetConfig.getTarget(exchange)//
.map(GeorchestraTargetConfig::headers)//
.ifPresent(mappings -> {
Optional<GeorchestraUser> user = GeorchestraUsers.resolve(exchange);
add(headers, "sec-userid", mappings.getUserid(), user.map(GeorchestraUser::getId));
add(headers, "sec-username", mappings.getUsername(), user.map(GeorchestraUser::getUsername));
add(headers, "sec-org", mappings.getOrg(), user.map(GeorchestraUser::getOrganization));
add(headers, "sec-email", mappings.getEmail(), user.map(GeorchestraUser::getEmail));
add(headers, "sec-firstname", mappings.getFirstname(), user.map(GeorchestraUser::getFirstName));
add(headers, "sec-lastname", mappings.getLastname(), user.map(GeorchestraUser::getLastName));
add(headers, "sec-tel", mappings.getTel(), user.map(GeorchestraUser::getTelephoneNumber));
public @Override Consumer<HttpHeaders> prepare(ServerWebExchange exchange) {
return headers -> {
GeorchestraTargetConfig.getTarget(exchange)//
.map(GeorchestraTargetConfig::headers)//
.ifPresent(mappings -> {
Optional<GeorchestraUser> user = GeorchestraUsers.resolve(exchange);
add(headers, "sec-userid", mappings.getUserid(), user.map(GeorchestraUser::getId));
add(headers, "sec-username", mappings.getUsername(), user.map(GeorchestraUser::getUsername));
add(headers, "sec-org", mappings.getOrg(), user.map(GeorchestraUser::getOrganization));
add(headers, "sec-email", mappings.getEmail(), user.map(GeorchestraUser::getEmail));
add(headers, "sec-firstname", mappings.getFirstname(), user.map(GeorchestraUser::getFirstName));
add(headers, "sec-lastname", mappings.getLastname(), user.map(GeorchestraUser::getLastName));
add(headers, "sec-tel", mappings.getTel(), user.map(GeorchestraUser::getTelephoneNumber));

List<String> roles = user.map(GeorchestraUser::getRoles).orElse(List.of()).stream()
.map(r -> r.startsWith("ROLE_") ? r : "ROLE_" + r).collect(Collectors.toList());
List<String> roles = user.map(GeorchestraUser::getRoles).orElse(List.of()).stream()
.map(r -> r.startsWith("ROLE_") ? r : "ROLE_" + r).collect(Collectors.toList());

add(headers, "sec-roles", mappings.getRoles(), roles);
add(headers, "sec-roles", mappings.getRoles(), roles);

add(headers, "sec-lastupdated", mappings.getLastUpdated(),
user.map(GeorchestraUser::getLastUpdated));
add(headers, "sec-address", mappings.getAddress(), user.map(GeorchestraUser::getPostalAddress));
add(headers, "sec-title", mappings.getTitle(), user.map(GeorchestraUser::getTitle));
add(headers, "sec-notes", mappings.getNotes(), user.map(GeorchestraUser::getNotes));
});
};
}
add(headers, "sec-lastupdated", mappings.getLastUpdated(),
user.map(GeorchestraUser::getLastUpdated));
add(headers, "sec-address", mappings.getAddress(), user.map(GeorchestraUser::getPostalAddress));
add(headers, "sec-title", mappings.getTitle(), user.map(GeorchestraUser::getTitle));
add(headers, "sec-notes", mappings.getNotes(), user.map(GeorchestraUser::getNotes));
});
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright (C) 2022 by the geOrchestra PSC
*
* This file is part of geOrchestra.
*
* geOrchestra is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* geOrchestra is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* geOrchestra. If not, see <http://www.gnu.org/licenses/>.
*/
package org.georchestra.gateway.filter.headers.providers;

import java.util.Optional;
import java.util.function.Consumer;

import org.georchestra.commons.security.SecurityHeaders;
import org.georchestra.ds.security.OrganizationsApiImpl;
import org.georchestra.gateway.filter.headers.HeaderContributor;
import org.georchestra.gateway.model.GeorchestraOrganizations;
import org.georchestra.gateway.model.GeorchestraTargetConfig;
import org.georchestra.gateway.model.GeorchestraUsers;
import org.georchestra.gateway.model.HeaderMappings;
import org.georchestra.security.model.GeorchestraUser;
import org.georchestra.security.model.Organization;
import org.springframework.http.HttpHeaders;
import org.springframework.web.server.ServerWebExchange;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

/**
* Contributes {@literal sec-user} and {@literal sec-organization}
* Base64-encoded JSON payloads, based on {@link HeaderMappings#getJsonUser()}
* and {@link HeaderMappings#getJsonOrganization()} matched-route headers
* configuration.
*
* @see GeorchestraUsers#resolve
* @see GeorchestraOrganizations#resolve
* @see GeorchestraTargetConfig
*/
public class JsonPayloadHeadersContributor extends HeaderContributor {

/**
* Encoder to create the JSON String value for a {@link GeorchestraUser}
* obtained from {@link OrganizationsApiImpl}
*/
private ObjectMapper encoder;

public JsonPayloadHeadersContributor() {
this.encoder = new ObjectMapper();
this.encoder.configure(SerializationFeature.INDENT_OUTPUT, Boolean.FALSE);
this.encoder.configure(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED, Boolean.FALSE);
this.encoder.setSerializationInclusion(Include.NON_NULL);
}

public @Override Consumer<HttpHeaders> prepare(ServerWebExchange exchange) {
return headers -> {
GeorchestraTargetConfig.getTarget(exchange)//
.map(GeorchestraTargetConfig::headers)//
.ifPresent(mappings -> {
Optional<GeorchestraUser> user = GeorchestraUsers.resolve(exchange);
Optional<Organization> org = GeorchestraOrganizations.resolve(exchange);

addJson(headers, "sec-user", mappings.getJsonUser(), user);
addJson(headers, "sec-organization", mappings.getJsonOrganization(), org);
});
};
}

private void addJson(HttpHeaders target, String headerName, Optional<Boolean> enabled, Optional<?> toEncode) {
if (enabled.orElse(false)) {
toEncode.map(this::encodeJson)//
.map(this::encodeBase64)//
.ifPresent(encoded -> target.add(headerName, encoded));
}
}

private String encodeJson(Object payloadObject) {
try {
return this.encoder.writer().writeValueAsString(payloadObject);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}

private String encodeBase64(String json) {
return SecurityHeaders.encodeBase64(json);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public class GeorchestraOrganizations {
static final String GEORCHESTRA_ORGANIZATION_KEY = GeorchestraOrganizations.class.getCanonicalName();

public static Optional<Organization> resolve(ServerWebExchange exchange) {
return Optional.ofNullable(exchange.getAttribute(GEORCHESTRA_ORGANIZATION_KEY)).map(Organization.class::cast);
return Optional.ofNullable(exchange.getAttributes().get(GEORCHESTRA_ORGANIZATION_KEY))
.map(Organization.class::cast);
}

public static void store(ServerWebExchange exchange, Organization org) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public class GeorchestraTargetConfig {
private List<RoleBasedAccessRule> accessRules;

public static Optional<GeorchestraTargetConfig> getTarget(ServerWebExchange exchange) {
return Optional.ofNullable(exchange.getAttributes().get(TARGET_CONFIG_KEY)).map(GeorchestraTargetConfig.class::cast);
return Optional.ofNullable(exchange.getAttributes().get(TARGET_CONFIG_KEY))
.map(GeorchestraTargetConfig.class::cast);
}

public static void setTarget(ServerWebExchange exchange, GeorchestraTargetConfig config) {
Expand Down
Loading

0 comments on commit 671f07b

Please sign in to comment.