From 359d60ffb895cac4784fdde2c85c7c25f2417ec9 Mon Sep 17 00:00:00 2001 From: joshua bauer Date: Tue, 23 May 2017 08:26:02 -0700 Subject: [PATCH] Added Basic auth to Swagger support. --- .../server/security/MapIdentityManager.java | 110 ++++++++++++++++++ .../proteus/services/SwaggerService.java | 63 +++++++++- src/main/resources/reference.conf | 11 ++ 3 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 src/main/java/io/sinistral/proteus/server/security/MapIdentityManager.java diff --git a/src/main/java/io/sinistral/proteus/server/security/MapIdentityManager.java b/src/main/java/io/sinistral/proteus/server/security/MapIdentityManager.java new file mode 100644 index 0000000..960c1ec --- /dev/null +++ b/src/main/java/io/sinistral/proteus/server/security/MapIdentityManager.java @@ -0,0 +1,110 @@ +/** + * + */ +package io.sinistral.proteus.server.security; + +import java.security.Principal; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import io.undertow.security.idm.Account; +import io.undertow.security.idm.Credential; +import io.undertow.security.idm.IdentityManager; +import io.undertow.security.idm.PasswordCredential; + +/** + * @author jbauer + */ +public class MapIdentityManager implements IdentityManager +{ + + private final Map identities; + + public MapIdentityManager(final Map identities) + { + this.identities = identities; + } + + @Override + public Account verify(Account account) + { + // An existing account so for testing assume still valid. + return account; + } + + @Override + public Account verify(String id, Credential credential) + { + Account account = getAccount(id); + if (account != null && verifyCredential(account, credential)) + { + return account; + } + + return null; + } + + @Override + public Account verify(Credential credential) + { + // TODO Auto-generated method stub + return null; + } + + private boolean verifyCredential(Account account, Credential credential) + { + if (credential instanceof PasswordCredential) + { + char[] password = ((PasswordCredential) credential).getPassword(); + char[] expectedPassword = identities.get(account.getPrincipal().getName()); + + return Arrays.equals(password, expectedPassword); + } + return false; + } + + private Account getAccount(final String id) + { + if (identities.containsKey(id)) + { + return new UserAccount(id); + } + return null; + } + + private class UserAccount implements Account + { + + private static final long serialVersionUID = -8234851531206339721L; + + private final Principal principal; + + public UserAccount(String id) + { + principal = new Principal() + { + + @Override + public String getName() + { + return id; + } + }; + } + + @Override + public Principal getPrincipal() + { + return principal; + } + + @Override + public Set getRoles() + { + return Collections.emptySet(); + } + } + +} diff --git a/src/main/java/io/sinistral/proteus/services/SwaggerService.java b/src/main/java/io/sinistral/proteus/services/SwaggerService.java index 493549e..d6a549b 100644 --- a/src/main/java/io/sinistral/proteus/services/SwaggerService.java +++ b/src/main/java/io/sinistral/proteus/services/SwaggerService.java @@ -11,6 +11,8 @@ import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -37,18 +39,26 @@ import com.typesafe.config.ConfigObject; import io.sinistral.proteus.server.endpoints.EndpointInfo; -import io.sinistral.proteus.server.swagger.ServerParameterExtension; +import io.sinistral.proteus.server.security.MapIdentityManager; +import io.sinistral.proteus.server.swagger.ServerParameterExtension; import io.swagger.jaxrs.ext.SwaggerExtension; import io.swagger.jaxrs.ext.SwaggerExtensions; import io.swagger.models.Info; -import io.swagger.models.SecurityRequirement; import io.swagger.models.Swagger; import io.swagger.models.auth.ApiKeyAuthDefinition; -import io.swagger.models.auth.SecuritySchemeDefinition; +import io.swagger.models.auth.BasicAuthDefinition; import io.undertow.attribute.ExchangeAttribute; import io.undertow.attribute.ExchangeAttributes; import io.undertow.predicate.Predicate; import io.undertow.predicate.Predicates; +import io.undertow.security.api.AuthenticationMechanism; +import io.undertow.security.api.AuthenticationMode; +import io.undertow.security.handlers.AuthenticationCallHandler; +import io.undertow.security.handlers.AuthenticationConstraintHandler; +import io.undertow.security.handlers.AuthenticationMechanismsHandler; +import io.undertow.security.handlers.SecurityInitialHandler; +import io.undertow.security.idm.IdentityManager; +import io.undertow.security.impl.BasicAuthenticationMechanism; import io.undertow.server.HandlerWrapper; import io.undertow.server.HttpHandler; import io.undertow.server.HttpServerExchange; @@ -257,6 +267,53 @@ public HttpHandler wrap(final HttpHandler handler) } } } + + if(swaggerSecurity.hasPath("basicRealms")) + { + List realms = swaggerSecurity.getObjectList("basicRealms"); + + for(ConfigObject realm : realms) + { + Config realmConfig = realm.toConfig(); + + final String name = realmConfig.getString("name"); + + List identities = realmConfig.getStringList("identities"); + + final Map identityMap = new HashMap<>(); + + identities.stream().forEach( i -> { + String[] identity = i.split(":"); + + identityMap.put(identity[0], identity[1].toCharArray()); + }); + + final IdentityManager identityManager = new MapIdentityManager(identityMap); + + log.debug("Adding basic handler for realm " + name + " with identities " + identityMap); + + + final HandlerWrapper wrapper = new HandlerWrapper() + { + @Override + public HttpHandler wrap(final HttpHandler handler) + { + HttpHandler authHandler = new AuthenticationCallHandler(handler); + authHandler = new AuthenticationConstraintHandler(authHandler); + final List mechanisms = Collections.singletonList(new BasicAuthenticationMechanism(name)); + authHandler = new AuthenticationMechanismsHandler(authHandler, mechanisms); + authHandler = new SecurityInitialHandler(AuthenticationMode.PRO_ACTIVE, identityManager, authHandler); + return authHandler; + } + }; + + BasicAuthDefinition authDefinition = new BasicAuthDefinition(); + swagger.addSecurityDefinition(name, authDefinition); + + registeredHandlerWrappers.put(name, wrapper); + + } + } this.reader = new io.sinistral.proteus.server.swagger.Reader(swagger); diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf index f3aa3aa..9a56766 100644 --- a/src/main/resources/reference.conf +++ b/src/main/resources/reference.conf @@ -94,6 +94,17 @@ swagger { value="123456789" } ] + +# basicRealms = +# [ +# { +# name = defaultBasic +# identities = +# [ +# "username:password" +# ] +# } +# ] }