added configuration of accepted secret per app
This commit is contained in:
parent
51fbda1161
commit
48347ea8f7
|
@ -55,6 +55,9 @@ public class ApplicationConfiguration {
|
|||
@NotEmpty @JsonProperty("persistence")
|
||||
PersistenceConfiguration persistenceConfiguration;
|
||||
|
||||
@JsonProperty("allowed-secrets")
|
||||
List<String> allowedSecretClasses = null;
|
||||
|
||||
public Set<GCubeExclude> excludes() {
|
||||
return excludes;
|
||||
}
|
||||
|
@ -83,6 +86,15 @@ public class ApplicationConfiguration {
|
|||
return this;
|
||||
}
|
||||
|
||||
public ApplicationConfiguration allowedSecrets(String ... classNames) {
|
||||
this.allowedSecretClasses = Arrays.asList(classNames);
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<String> allowedSecrets(){
|
||||
return this.allowedSecretClasses;
|
||||
}
|
||||
|
||||
public ApplicationConfiguration context(String context) {
|
||||
this.context = context;
|
||||
return this;
|
||||
|
@ -154,12 +166,11 @@ public class ApplicationConfiguration {
|
|||
|
||||
if (!msgs.isEmpty())
|
||||
throw new IllegalStateException("invalid configuration: "+msgs);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(description, excludes, group, includes, name, proxable, version);
|
||||
return Objects.hash(description, excludes, group, includes, name, proxable, version, allowedSecretClasses);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -176,7 +187,14 @@ public class ApplicationConfiguration {
|
|||
&& Objects.equals(includes, other.includes) && Objects.equals(name, other.name)
|
||||
&& proxable == other.proxable && Objects.equals(version, other.version);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ApplicationConfiguration [name=" + name + ", group=" + group + ", version=" + version + ", description="
|
||||
+ description + ", context=" + context + ", proxable=" + proxable + ", excludes=" + excludes
|
||||
+ ", includes=" + includes + ", persistenceConfiguration=" + persistenceConfiguration
|
||||
+ ", allowedSecretClasses=" + this.allowedSecretClasses + "]";
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -39,6 +39,8 @@ public class ApplicationConfigurationBinder {
|
|||
ObjectMapper mapper = new ObjectMapper();
|
||||
String mapAsString = mapper.writeValueAsString(yaml.load(stream));
|
||||
|
||||
System.out.println(mapAsString);
|
||||
|
||||
ApplicationConfiguration conf = mapper.readValue(mapAsString, ApplicationConfiguration.class);
|
||||
|
||||
if (conf.persistenceConfiguration() == null) {
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
package org.gcube.smartgears.context.application;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import org.gcube.common.events.Hub;
|
||||
import org.gcube.common.security.secrets.Secret;
|
||||
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
|
||||
import org.gcube.smartgears.context.Properties;
|
||||
import org.gcube.smartgears.context.container.ContainerContext;
|
||||
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
|
||||
import org.gcube.smartgears.persistence.PersistenceWriter;
|
||||
import org.gcube.smartgears.security.AuthorizationProvider;
|
||||
import org.gcube.smartgears.security.secrets.SecretFactory;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
|
||||
|
@ -92,4 +95,11 @@ public interface ApplicationContext {
|
|||
*/
|
||||
Path appSpecificConfigurationFolder();
|
||||
|
||||
/**
|
||||
* returns the list of factory secrets ordered by priority
|
||||
*
|
||||
* @returnA Linked List
|
||||
*/
|
||||
List<SecretFactory<? extends Secret>> allowedSecretFactories();
|
||||
|
||||
}
|
||||
|
|
|
@ -5,11 +5,16 @@ import static org.gcube.smartgears.Constants.profile_file_path;
|
|||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.gcube.common.events.Hub;
|
||||
import org.gcube.common.security.secrets.Secret;
|
||||
import org.gcube.smartgears.configuration.PersistenceConfiguration;
|
||||
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
|
||||
import org.gcube.smartgears.context.Properties;
|
||||
|
@ -17,6 +22,9 @@ import org.gcube.smartgears.context.container.ContainerContext;
|
|||
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
|
||||
import org.gcube.smartgears.persistence.PersistenceWriter;
|
||||
import org.gcube.smartgears.security.AuthorizationProvider;
|
||||
import org.gcube.smartgears.security.secrets.GCubeKeyCloakSecretFactory;
|
||||
import org.gcube.smartgears.security.secrets.LegacyGCubeTokenSecretFactory;
|
||||
import org.gcube.smartgears.security.secrets.SecretFactory;
|
||||
import org.gcube.smartgears.utils.Utils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -45,6 +53,11 @@ public class DefaultApplicationContext implements ApplicationContext {
|
|||
|
||||
private final static String APPS_CONFIG__DIR = "config/apps";
|
||||
|
||||
private static final List<SecretFactory<? extends Secret>> defaultSecretFactories =
|
||||
new LinkedList<SecretFactory<? extends Secret>>(Arrays.asList(new GCubeKeyCloakSecretFactory(), new LegacyGCubeTokenSecretFactory()));
|
||||
|
||||
private List<SecretFactory<? extends Secret>> allowedSecretFactories = null;
|
||||
|
||||
/**
|
||||
* Crates an intance with mandatory parameters
|
||||
* @param container the container context
|
||||
|
@ -86,6 +99,27 @@ public class DefaultApplicationContext implements ApplicationContext {
|
|||
this.lifecycle = lifecycle;
|
||||
this.appSpecificConfigurationFolder = getApplicationSpecificConfig();
|
||||
this.properties=properties;
|
||||
|
||||
if (this.configuration.allowedSecrets()!=null && this.configuration.allowedSecrets().size()>0) {
|
||||
this.allowedSecretFactories = new LinkedList<SecretFactory<? extends Secret>>();
|
||||
for (String clazz : this.configuration.allowedSecrets() )
|
||||
try {
|
||||
Object obj = Class.forName(clazz).getConstructor().newInstance();
|
||||
@SuppressWarnings("unchecked")
|
||||
SecretFactory<? extends Secret> factory = SecretFactory.class.cast(obj);
|
||||
this.allowedSecretFactories.add(factory);
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
|
||||
| InvocationTargetException | NoSuchMethodException | SecurityException
|
||||
| ClassNotFoundException e) {
|
||||
log.warn("declared secret factory {} instantiation error",clazz, e);
|
||||
} catch (ClassCastException cc) {
|
||||
log.warn("declared secret factory {} is not implementation of SecretFacory class",clazz, cc);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.allowedSecretFactories == null || this.allowedSecretFactories.size()==0 )
|
||||
this.allowedSecretFactories = defaultSecretFactories;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -93,10 +127,10 @@ public class DefaultApplicationContext implements ApplicationContext {
|
|||
* @param context the other instance
|
||||
*/
|
||||
public DefaultApplicationContext(ApplicationContext context) {
|
||||
this(context.id(), context.persistence(), context.container(),context.application(),context.configuration(),context.events(), context.lifecycle(), new Properties(context.properties()));
|
||||
this(context.id(), context.persistence(), context.container(),context.application(),context.configuration(),context.events(), context.lifecycle(), new Properties(context.properties()), context.allowedSecretFactories());
|
||||
}
|
||||
|
||||
private DefaultApplicationContext(String id, PersistenceWriter writer, ContainerContext container,ServletContext sctx,ApplicationConfiguration configuration, Hub hub, ApplicationLifecycle lifecycle, Properties properties) {
|
||||
private DefaultApplicationContext(String id, PersistenceWriter writer, ContainerContext container,ServletContext sctx,ApplicationConfiguration configuration, Hub hub, ApplicationLifecycle lifecycle, Properties properties, List<SecretFactory<? extends Secret>> allowedSecretFactories) {
|
||||
this.id = id;
|
||||
this.container=container;
|
||||
this.sctx = sctx;
|
||||
|
@ -105,6 +139,7 @@ public class DefaultApplicationContext implements ApplicationContext {
|
|||
this.lifecycle = lifecycle;
|
||||
this.properties=properties;
|
||||
this.persistenceWriter = writer;
|
||||
this.allowedSecretFactories = allowedSecretFactories;
|
||||
|
||||
}
|
||||
|
||||
|
@ -189,4 +224,9 @@ public class DefaultApplicationContext implements ApplicationContext {
|
|||
|
||||
return appSpecificConfigurationPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SecretFactory<? extends Secret>> allowedSecretFactories() {
|
||||
return this.allowedSecretFactories;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,16 @@
|
|||
package org.gcube.smartgears.handlers.application.request;
|
||||
|
||||
import static org.gcube.smartgears.Constants.token_header;
|
||||
import static org.gcube.smartgears.handlers.application.request.RequestError.application_failed_error;
|
||||
import static org.gcube.smartgears.handlers.application.request.RequestError.application_unavailable_error;
|
||||
import static org.gcube.smartgears.handlers.application.request.RequestError.invalid_request_error;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.gcube.common.keycloak.KeycloakClient;
|
||||
import org.gcube.common.keycloak.KeycloakClientException;
|
||||
import org.gcube.common.keycloak.KeycloakClientFactory;
|
||||
import org.gcube.common.security.ContextBean;
|
||||
import org.gcube.common.security.ContextBean.Type;
|
||||
import org.gcube.common.security.providers.SecretManagerProvider;
|
||||
import org.gcube.common.security.secrets.GCubeSecret;
|
||||
import org.gcube.common.security.secrets.Secret;
|
||||
import org.gcube.common.security.secrets.UmaTokenSecret;
|
||||
import org.gcube.smartgears.Constants;
|
||||
import org.gcube.smartgears.configuration.Mode;
|
||||
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
|
||||
|
@ -23,7 +18,8 @@ import org.gcube.smartgears.context.application.ApplicationContext;
|
|||
import org.gcube.smartgears.handlers.application.RequestEvent;
|
||||
import org.gcube.smartgears.handlers.application.RequestHandler;
|
||||
import org.gcube.smartgears.handlers.application.ResponseEvent;
|
||||
import org.gcube.smartgears.security.SimpleCredentials;
|
||||
import org.gcube.smartgears.security.secrets.SecretFactory;
|
||||
import org.gcube.smartgears.security.secrets.exceptions.SecretNotFoundException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -31,8 +27,6 @@ public class RequestValidator extends RequestHandler {
|
|||
|
||||
private static Logger log = LoggerFactory.getLogger(RequestValidator.class);
|
||||
|
||||
private static final String BEARER_AUTH_PREFIX ="Bearer";
|
||||
|
||||
private ApplicationContext appContext;
|
||||
|
||||
@Override
|
||||
|
@ -124,37 +118,23 @@ public class RequestValidator extends RequestHandler {
|
|||
}
|
||||
|
||||
private Secret getSecret(RequestEvent call){
|
||||
String token = call.request().getParameter(token_header)==null? call.request().getHeader(token_header):call.request().getParameter(token_header);
|
||||
String authHeader = call.request().getHeader(Constants.authorization_header);
|
||||
|
||||
|
||||
log.trace("authorization header is {}",authHeader);
|
||||
log.trace("token header is {}", token);
|
||||
|
||||
log.info("d4s-user set to {} ", call.request().getHeader("d4s-user"));
|
||||
|
||||
String umaToken = null;
|
||||
if (authHeader!=null && !authHeader.isEmpty())
|
||||
if (authHeader.startsWith(BEARER_AUTH_PREFIX))
|
||||
umaToken = authHeader.substring(BEARER_AUTH_PREFIX.length()).trim();
|
||||
|
||||
|
||||
Secret secret = null;
|
||||
if (umaToken!=null) {
|
||||
secret = new UmaTokenSecret(umaToken);
|
||||
SimpleCredentials credentials = (SimpleCredentials)appContext.authorizationProvider().getCredentials();
|
||||
KeycloakClient client = KeycloakClientFactory.newInstance();
|
||||
for (SecretFactory<? extends Secret> factory: call.context().allowedSecretFactories()) {
|
||||
try {
|
||||
if(!client.isAccessTokenVerified(secret.getContext(), credentials.getClientID(), credentials.getSecret(), umaToken))
|
||||
RequestError.request_not_authorized_error.fire("access token verification error");
|
||||
}catch (KeycloakClientException e) {
|
||||
RequestError.internal_server_error.fire("error contacting keycloak client", e);
|
||||
}
|
||||
} else if (token!=null && !token.isEmpty())
|
||||
try {
|
||||
secret = new GCubeSecret(token);
|
||||
}catch(Throwable t) {
|
||||
RequestError.request_not_authorized_error.fire("gcube token verification error ("+t.getMessage()+")");
|
||||
secret = factory.create(call.request());
|
||||
if (!secret.isValid())
|
||||
RequestError.request_not_authorized_error.fire("authorization with secret "+factory.getSecretClass().getName()+" is not valid ");
|
||||
} catch (SecretNotFoundException e) {
|
||||
log.info("authorization with secret {} not found", factory.getSecretClass().getName());
|
||||
} catch (Throwable t) {
|
||||
log.warn("geenric error with secret {}", factory.getSecretClass().getName(), t);
|
||||
}
|
||||
}
|
||||
|
||||
if (Objects.isNull(secret))
|
||||
RequestError.request_not_authorized_error.fire("call not authorized");
|
||||
|
||||
return secret;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.gcube.smartgears.security;
|
|||
|
||||
import java.util.Set;
|
||||
|
||||
import org.gcube.common.security.credentials.Credentials;
|
||||
import org.gcube.common.security.secrets.Secret;
|
||||
|
||||
public interface AuthorizationProvider {
|
||||
|
@ -10,6 +9,4 @@ public interface AuthorizationProvider {
|
|||
Set<String> getContexts();
|
||||
|
||||
Secret getSecretForContext(String context);
|
||||
|
||||
Credentials getCredentials();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package org.gcube.smartgears.security.secrets;
|
||||
|
||||
import org.gcube.common.security.secrets.UmaTokenSecret;
|
||||
import org.gcube.smartgears.Constants;
|
||||
import org.gcube.smartgears.security.secrets.exceptions.SecretNotFoundException;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
public class GCubeKeyCloakSecretFactory implements SecretFactory<UmaTokenSecret> {
|
||||
|
||||
private static final String BEARER_AUTH_PREFIX ="Bearer";
|
||||
|
||||
@Override
|
||||
public UmaTokenSecret create(HttpServletRequest request) throws SecretNotFoundException {
|
||||
String authHeader = request.getHeader(Constants.authorization_header);
|
||||
String umaToken = null;
|
||||
if (authHeader!=null && !authHeader.isEmpty() && authHeader.startsWith(BEARER_AUTH_PREFIX)) {
|
||||
umaToken = authHeader.substring(BEARER_AUTH_PREFIX.length()).trim();
|
||||
return new UmaTokenSecret(umaToken);
|
||||
} else throw new SecretNotFoundException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<UmaTokenSecret> getSecretClass() {
|
||||
return UmaTokenSecret.class;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package org.gcube.smartgears.security.secrets;
|
||||
|
||||
import static org.gcube.smartgears.Constants.token_header;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.gcube.common.security.secrets.GCubeSecret;
|
||||
import org.gcube.smartgears.security.secrets.exceptions.SecretNotFoundException;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
public class LegacyGCubeTokenSecretFactory implements SecretFactory<GCubeSecret> {
|
||||
|
||||
@Override
|
||||
public GCubeSecret create(HttpServletRequest request) throws SecretNotFoundException {
|
||||
String token = request.getParameter(token_header)==null? request.getHeader(token_header):request.getParameter(token_header);
|
||||
if (Objects.isNull(token) || token.isBlank()) throw new SecretNotFoundException();
|
||||
return new GCubeSecret(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<GCubeSecret> getSecretClass() {
|
||||
return GCubeSecret.class;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.gcube.smartgears.security.secrets;
|
||||
|
||||
import org.gcube.common.security.secrets.Secret;
|
||||
import org.gcube.smartgears.security.secrets.exceptions.SecretNotFoundException;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
public interface SecretFactory<T extends Secret> {
|
||||
|
||||
Class<T> getSecretClass();
|
||||
|
||||
T create(HttpServletRequest request) throws SecretNotFoundException;
|
||||
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package org.gcube.smartgears.security.secrets.exceptions;
|
||||
|
||||
public class SecretNotFoundException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
}
|
|
@ -2,19 +2,28 @@ package test;
|
|||
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
|
||||
import org.gcube.smartgears.configuration.application.ApplicationConfigurationBinder;
|
||||
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
|
||||
import org.gcube.smartgears.configuration.container.ContainerConfigurationBinder;
|
||||
import org.junit.Test;
|
||||
|
||||
public class BinderTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void bindConfig() throws Exception {
|
||||
|
||||
public void bindContainerConfig() throws Exception {
|
||||
InputStream stream = BinderTest.class.getResourceAsStream("/container.ini");
|
||||
|
||||
ContainerConfiguration conf = new ContainerConfigurationBinder().load(stream);
|
||||
System.out.println(conf.toString());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void bindApplicationConfig() throws Exception {
|
||||
InputStream stream = BinderTest.class.getResourceAsStream("/application.yaml");
|
||||
|
||||
ApplicationConfiguration conf = new ApplicationConfigurationBinder().load(stream);
|
||||
System.out.println(conf.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,10 @@ excludes:
|
|||
- handlers: [H1, H2]
|
||||
path: /trip
|
||||
#not mandatory
|
||||
allowed-secrets:
|
||||
- org.gcube.smartgears.security.secrets.GCubeKeyCloakSecretFactory
|
||||
- org.gcube.smartgears.security.secrets.LegacyGCubeTokenSecretFactory
|
||||
#not mandatory
|
||||
persistence:
|
||||
implementationClass: org.gcube.smartgears.persistence.LocalWriter
|
||||
writerConfiguration:
|
||||
|
|
Loading…
Reference in New Issue