From f9486b50bbf6f92a4549203c1ede21ba912989c0 Mon Sep 17 00:00:00 2001 From: Michael DÜrre Date: Wed, 17 Feb 2021 08:48:52 +0100 Subject: add unit tests for oauth-provider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add unit tests and some fixes for oauth provider Issue-ID: CCSDK-3169 Signed-off-by: Michael DÜrre Change-Id: I88c5b2123df7e0c6e49b087c3cc7d24130be5bff --- .../sdnr/wt/oauthprovider/OAuth2Realm.java | 3 +- .../sdnr/wt/oauthprovider/data/Config.java | 43 ++--- .../wt/oauthprovider/data/OAuthProviderConfig.java | 32 ++-- .../sdnr/wt/oauthprovider/data/OdlPolicy.java | 2 +- .../wt/oauthprovider/http/AuthHttpServlet.java | 120 +++++++------- .../wt/oauthprovider/providers/AuthService.java | 92 +++++++---- .../providers/GitlabProviderService.java | 173 +++++++++++++++++++++ .../providers/KeycloakProviderService.java | 21 ++- .../providers/MdSalAuthorizationStore.java | 106 +++++++++++++ .../providers/NextcloudProviderService.java | 12 ++ .../providers/OAuthProviderFactory.java | 4 +- .../wt/oauthprovider/providers/TokenCreator.java | 34 ++-- 12 files changed, 506 insertions(+), 136 deletions(-) create mode 100644 sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/GitlabProviderService.java create mode 100644 sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/MdSalAuthorizationStore.java (limited to 'sdnr/wt/oauth-provider/provider-jar/src/main/java/org') diff --git a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/OAuth2Realm.java b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/OAuth2Realm.java index a3e4bcc2a..0a40e8ddc 100644 --- a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/OAuth2Realm.java +++ b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/OAuth2Realm.java @@ -50,9 +50,8 @@ public class OAuth2Realm extends TokenAuthRealm { super(); super.setName(REALM_NAME); this.config = Config.getInstance(); - this.tokenCreator = TokenCreator.getInstance(); + this.tokenCreator = TokenCreator.getInstance(this.config); LOG.info("instantiated"); - } @Override diff --git a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/Config.java b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/Config.java index ba26106c9..a71f4c7dc 100644 --- a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/Config.java +++ b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/Config.java @@ -48,16 +48,16 @@ public class Config { private List providers; - private String host; private String redirectUri; private String supportOdlUsers; private String tokenSecret; private String tokenIssuer; + private String publicUrl; @Override public String toString() { - return "Config [providers=" + providers + ", host=" + host + ", redirectUri=" + redirectUri + return "Config [providers=" + providers + ", redirectUri=" + redirectUri + ", supportOdlUsers=" + supportOdlUsers + ", tokenSecret=" + tokenSecret + ", tokenIssuer=" + tokenIssuer + "]"; } @@ -72,14 +72,6 @@ public class Config { this.providers = providers; } - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - public String getRedirectUri() { return redirectUri; } @@ -112,6 +104,15 @@ public class Config { this.tokenIssuer = tokenIssuer; } + + public String getPublicUrl() { + return publicUrl; + } + + public void setPublicUrl(String publicUrl) { + this.publicUrl = publicUrl; + } + @JsonIgnore private void handleEnvironmentVars() { if (isEnvExpression(tokenIssuer)) { @@ -120,8 +121,8 @@ public class Config { if (isEnvExpression(tokenSecret)) { this.tokenSecret = getProperty(tokenSecret, null); } - if (isEnvExpression(host)) { - this.host = getProperty(host, null); + if (isEnvExpression(publicUrl)) { + this.publicUrl = getProperty(publicUrl, null); } if (isEnvExpression(redirectUri)) { this.redirectUri = getProperty(redirectUri, null); @@ -139,9 +140,12 @@ public class Config { if (tokenSecret == null || tokenSecret.isEmpty()) { this.tokenSecret = DEFAULT_TOKENSECRET; } - if (redirectUri == null || redirectUri.isEmpty()) { + if (redirectUri == null || redirectUri.isEmpty() || "null".equals(redirectUri)) { this.redirectUri = DEFAULT_REDIRECTURI; } + if (publicUrl != null && (publicUrl.isEmpty() || "null".equals(publicUrl))) { + this.publicUrl = null; + } if (supportOdlUsers == null || supportOdlUsers.isEmpty()) { this.supportOdlUsers = DEFAULT_SUPPORTODLUSERS; } @@ -150,11 +154,12 @@ public class Config { static boolean isEnvExpression(String key) { return key != null && key.contains(ENVVARIABLE); } - - private static String generateSecret() { + public static String generateSecret() { + return generateSecret(30); + } + public static String generateSecret(int targetStringLength) { int leftLimit = 48; // numeral '0' int rightLimit = 122; // letter 'z' - int targetStringLength = 30; Random random = new Random(); String generatedString = random.ints(leftLimit, rightLimit + 1) @@ -226,10 +231,12 @@ public class Config { } - public static Config getInstance() throws IOException { + return getInstance(DEFAULT_CONFIGFILENAME); + } + public static Config getInstance(String filename) throws IOException { if(_instance==null) { - _instance = load(DEFAULT_CONFIGFILENAME); + _instance = load(filename); } return _instance; } diff --git a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthProviderConfig.java b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthProviderConfig.java index e4d67432f..3f1673c93 100644 --- a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthProviderConfig.java +++ b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthProviderConfig.java @@ -22,35 +22,39 @@ package org.onap.ccsdk.features.sdnr.wt.oauthprovider.data; import com.fasterxml.jackson.annotation.JsonIgnore; +import java.util.HashMap; +import java.util.Map; import org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers.OAuthProviderFactory.OAuthProvider; public class OAuthProviderConfig { - private String host; + private String url; private String clientId; private String secret; private String id; private String title; private String scope; private OAuthProvider type; + private Map roleMapping; public OAuthProvider getType() { return type; } - public OAuthProviderConfig(String id, String host, String clientId, String secret, String scope, + public OAuthProviderConfig(String id, String url, String clientId, String secret, String scope, String title) { this.id = id; - this.host = host; + this.url = url; this.clientId = clientId; this.secret = secret; this.scope = scope; this.title = title; + this.roleMapping = new HashMap<>(); } @Override public String toString() { - return "OAuthProviderConfig [host=" + host + ", clientId=" + clientId + ", secret=" + secret + ", id=" + id + return "OAuthProviderConfig [host=" + url + ", clientId=" + clientId + ", secret=" + secret + ", id=" + id + ", title=" + title + ", scope=" + scope + ", type=" + type + "]"; } @@ -62,8 +66,8 @@ public class OAuthProviderConfig { this(null, null, null, null, null, null); } - public void setHost(String host) { - this.host = host; + public void setUrl(String url) { + this.url = url; } public void setClientId(String clientId) { @@ -90,8 +94,8 @@ public class OAuthProviderConfig { return this.id; } - public String getHost() { - return this.host; + public String getUrl() { + return this.url; } public String getClientId() { @@ -110,13 +114,21 @@ public class OAuthProviderConfig { return this.scope; } + public Map getRoleMapping() { + return roleMapping; + } + + public void setRoleMapping(Map roleMapping) { + this.roleMapping = roleMapping; + } + @JsonIgnore public void handleEnvironmentVars() { if (Config.isEnvExpression(id)) { this.id = Config.getProperty(id, null); } - if (Config.isEnvExpression(host)) { - this.host = Config.getProperty(host, null); + if (Config.isEnvExpression(url)) { + this.url = Config.getProperty(url, null); } if (Config.isEnvExpression(clientId)) { this.clientId = Config.getProperty(clientId, null); diff --git a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OdlPolicy.java b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OdlPolicy.java index 28ef3b3b0..19eb4b68e 100644 --- a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OdlPolicy.java +++ b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OdlPolicy.java @@ -68,7 +68,7 @@ public class OdlPolicy { private boolean patch; public PolicyMethods() { - + this(false, false, false, false, false); } public PolicyMethods(boolean get, boolean post, boolean put, boolean del, boolean patch) { diff --git a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/AuthHttpServlet.java b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/AuthHttpServlet.java index f5d344d41..cd4239081 100644 --- a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/AuthHttpServlet.java +++ b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/AuthHttpServlet.java @@ -48,11 +48,12 @@ import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OdlPolicy; import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UserTokenPayload; import org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers.AuthService; import org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers.AuthService.PublicOAuthProviderConfig; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers.MdSalAuthorizationStore; import org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers.OAuthProviderFactory; import org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers.TokenCreator; -import org.opendaylight.aaa.api.IDMStoreException; import org.opendaylight.aaa.api.IdMService; import org.opendaylight.aaa.shiro.filters.backport.BearerToken; +import org.opendaylight.mdsal.binding.api.DataBroker; import org.opendaylight.yang.gen.v1.urn.opendaylight.aaa.app.config.rev170619.ShiroConfiguration; import org.opendaylight.yang.gen.v1.urn.opendaylight.aaa.app.config.rev170619.shiro.configuration.Main; import org.opendaylight.yang.gen.v1.urn.opendaylight.aaa.app.config.rev170619.shiro.configuration.Urls; @@ -68,11 +69,14 @@ public class AuthHttpServlet extends HttpServlet { //private static final String LOGOUTURI = BASEURI + "/logout"; private static final String PROVIDERSURI = BASEURI + "/providers"; public static final String REDIRECTURI = BASEURI + "/redirect"; + private static final String REDIRECTURI_FORMAT = REDIRECTURI + "/%s"; private static final String POLICIESURI = BASEURI + "/policies"; //private static final String PROVIDERID_REGEX = "^\\" + BASEURI + "\\/providers\\/([^\\/]+)$"; private static final String REDIRECTID_REGEX = "^\\" + BASEURI + "\\/redirect\\/([^\\/]+)$"; + private static final String LOGIN_REDIRECT_REGEX = "^\\" + LOGINURI + "\\/([^\\/]+)$"; //private static final Pattern PROVIDERID_PATTERN = Pattern.compile(PROVIDERID_REGEX); private static final Pattern REDIRECTID_PATTERN = Pattern.compile(REDIRECTID_REGEX); + private static final Pattern LOGIN_REDIRECT_PATTERN = Pattern.compile(LOGIN_REDIRECT_REGEX); private static final String DEFAULT_DOMAIN = "sdn"; private static final String HEAEDER_AUTHORIZATION = "Authorization"; @@ -83,6 +87,7 @@ public class AuthHttpServlet extends HttpServlet { "org.opendaylight.aaa.shiro.filters.ODLHttpAuthenticationFilter2"; private static final String CLASSNAME_ODLMDSALAUTH = "org.opendaylight.aaa.shiro.realm.MDSALDynamicAuthorizationFilter"; + public static final String LOGIN_REDIRECT_FORMAT = LOGINURI + "/%s"; private final ObjectMapper mapper; /* state <=> AuthProviderService> */ @@ -92,15 +97,17 @@ public class AuthHttpServlet extends HttpServlet { private final TokenCreator tokenCreator; private final Config config; private ShiroConfiguration shiroConfiguration; + private DataBroker dataBroker; + private MdSalAuthorizationStore mdsalAuthStore; public AuthHttpServlet() throws IOException { - this.tokenCreator = TokenCreator.getInstance(); this.config = Config.getInstance(); + this.tokenCreator = TokenCreator.getInstance(this.config); this.mapper = new ObjectMapper(); this.providerStore = new HashMap<>(); for (OAuthProviderConfig pc : config.getProviders()) { this.providerStore.put(pc.getId(), OAuthProviderFactory.create(pc.getType(), pc, - this.config.getRedirectUri(), TokenCreator.getInstance())); + this.config.getRedirectUri(), TokenCreator.getInstance(this.config))); } } @@ -117,12 +124,19 @@ public class AuthHttpServlet extends HttpServlet { this.shiroConfiguration = shiroConfiguration; } + public void setDataBroker(DataBroker dataBroker) { + this.dataBroker = dataBroker; + this.mdsalAuthStore = new MdSalAuthorizationStore(this.dataBroker); + } + @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { LOG.debug("GET request for {}", req.getRequestURI()); - fillHost(req); + getHost(req); if (PROVIDERSURI.equals(req.getRequestURI())) { this.sendResponse(resp, HttpServletResponse.SC_OK, getConfigs(this.providerStore.values())); + } else if (req.getRequestURI().startsWith(LOGINURI)) { + this.handleLoginRedirect(req, resp); } else if (POLICIESURI.equals(req.getRequestURI())) { this.sendResponse(resp, HttpServletResponse.SC_OK, this.getPoliciesForUser(req)); } else if (req.getRequestURI().startsWith(REDIRECTURI)) { @@ -133,6 +147,22 @@ public class AuthHttpServlet extends HttpServlet { } + private void handleLoginRedirect(HttpServletRequest req, HttpServletResponse resp) throws IOException { + final String uri = req.getRequestURI(); + final Matcher matcher = LOGIN_REDIRECT_PATTERN.matcher(uri); + if (matcher.find()) { + final String id = matcher.group(1); + AuthService provider = this.providerStore.getOrDefault(id, null); + if (provider != null) { + //provider.setLocalHostUrl(getHost(req)); + String redirectUrl = getHost(req) + String.format(REDIRECTURI_FORMAT, id); + provider.sendLoginRedirectResponse(resp, redirectUrl); + return; + } + } + this.sendResponse(resp, HttpServletResponse.SC_NOT_FOUND, ""); + } + /** * find out what urls can be accessed by user and which are forbidden * @@ -161,8 +191,13 @@ public class AuthHttpServlet extends HttpServlet { try { final String authClass = getAuthClass(matcher.group(1)); Optional policy = Optional.empty(); - if (authClass.equals(CLASSNAME_ODLBASICAUTH) - || authClass.equals(CLASSNAME_ODLBEARERANDBASICAUTH)) { + //anon access allowed + if (authClass == null) { + policy = Optional.of(OdlPolicy.allowAll(urlRule.getPairKey())); + } else if (authClass.equals(CLASSNAME_ODLBASICAUTH)) { + policy = isBasic(req) ? this.getTokenBasedPolicy(urlRule, matcher, data) + : Optional.of(OdlPolicy.denyAll(urlRule.getPairKey())); + } else if (authClass.equals(CLASSNAME_ODLBEARERANDBASICAUTH)) { policy = this.getTokenBasedPolicy(urlRule, matcher, data); } else if (authClass.equals(CLASSNAME_ODLMDSALAUTH)) { policy = this.getMdSalBasedPolicy(urlRule, matcher, data); @@ -172,6 +207,7 @@ public class AuthHttpServlet extends HttpServlet { } else { LOG.warn("unable to get policy for authClass {} for entry {}", authClass, urlRule.getPairValue()); + policies.add(OdlPolicy.denyAll(urlRule.getPairKey())); } } catch (NoDefinitionFoundException e) { LOG.warn("unknown authClass: ", e); @@ -188,20 +224,24 @@ public class AuthHttpServlet extends HttpServlet { } /** - * extract policy rule for user from MD-SAL - * not yet supported + * extract policy rule for user from MD-SAL not yet supported + * * @param urlRule * @param matcher * @param data * @return */ private Optional getMdSalBasedPolicy(Urls urlRule, Matcher matcher, UserTokenPayload data) { - + if (this.mdsalAuthStore != null) { + return data != null ? this.mdsalAuthStore.getPolicy(urlRule.getPairKey(), data.getRoles()) + : Optional.of(OdlPolicy.denyAll(urlRule.getPairKey())); + } return Optional.empty(); } /** * extract policy rule for user from url rules of config + * * @param urlRule * @param matcher * @param data @@ -209,25 +249,27 @@ public class AuthHttpServlet extends HttpServlet { */ private Optional getTokenBasedPolicy(Urls urlRule, Matcher matcher, UserTokenPayload data) { final String url = urlRule.getPairKey(); - if (!urlRule.getPairValue().contains(",")) { + final String rule = urlRule.getPairValue(); + if (!rule.contains(",")) { LOG.debug("found rule without roles for '{}'", matcher.group(1)); //not important if anon or authcXXX if (data != null || "anon".equals(matcher.group(1))) { return Optional.of(OdlPolicy.allowAll(url)); } - } else if (data != null) { + } + if (data != null) { LOG.debug("found rule with roles '{}'", matcher.group(4)); if ("roles".equals(matcher.group(2))) { if (this.rolesMatch(data.getRoles(), Arrays.asList(matcher.group(4).split(",")), false)) { - Optional.of(OdlPolicy.allowAll(url)); + return Optional.of(OdlPolicy.allowAll(url)); } else { - Optional.of(OdlPolicy.denyAll(url)); + return Optional.of(OdlPolicy.denyAll(url)); } } else if ("anyroles".equals(matcher.group(2))) { if (this.rolesMatch(data.getRoles(), Arrays.asList(matcher.group(4).split(",")), true)) { - Optional.of(OdlPolicy.allowAll(url)); + return Optional.of(OdlPolicy.allowAll(url)); } else { - Optional.of(OdlPolicy.denyAll(url)); + return Optional.of(OdlPolicy.denyAll(url)); } } else { LOG.warn("unable to detect url role value: {}", urlRule.getPairValue()); @@ -252,7 +294,7 @@ public class AuthHttpServlet extends HttpServlet { private UserTokenPayload getUserInfo(HttpServletRequest req) { if (isBearer(req)) { - UserTokenPayload data = TokenCreator.getInstance().decode(req); + UserTokenPayload data = TokenCreator.getInstance(this.config).decode(req); if (data != null) { return data; } @@ -317,8 +359,8 @@ public class AuthHttpServlet extends HttpServlet { } - private void fillHost(HttpServletRequest req) { - String hostUrl = this.config.getHost(); + public String getHost(HttpServletRequest req) { + String hostUrl = this.config.getPublicUrl(); if (hostUrl == null) { final String tmp = req.getRequestURL().toString(); final String regex = "^(http[s]{0,1}:\\/\\/[^\\/]+)"; @@ -326,16 +368,17 @@ public class AuthHttpServlet extends HttpServlet { final Matcher matcher = pattern.matcher(tmp); if (matcher.find()) { hostUrl = matcher.group(1); - this.config.setHost(hostUrl); } } + LOG.info("host={}", hostUrl); + return hostUrl; } private List getConfigs(Collection values) { List configs = new ArrayList<>(); for (AuthService svc : values) { - configs.add(svc.getConfig(this.config.getHost())); + configs.add(svc.getConfig()); } return configs; } @@ -353,8 +396,8 @@ public class AuthHttpServlet extends HttpServlet { if (matcher.find()) { AuthService provider = this.providerStore.getOrDefault(matcher.group(1), null); if (provider != null) { - provider.setLocalHostUrl(this.config.getHost()); - provider.handleRedirect(req, resp); + //provider.setLocalHostUrl(getHost(req)); + provider.handleRedirect(req, resp, getHost(req)); return; } } @@ -382,33 +425,6 @@ public class AuthHttpServlet extends HttpServlet { resp.sendError(HttpServletResponse.SC_NOT_FOUND); } - @Override - protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - // final String uri = req.getRequestURI(); - // final Matcher matcher = PROVIDERID_PATTERN.matcher(uri); - // if (matcher.find()) { - // final String id = matcher.group(1); - // final OAuthProviderConfig config = this.mapper.readValue(req.getInputStream(), OAuthProviderConfig.class); - // //this.providerStore.put(id, config); - // sendResponse(resp, HttpServletResponse.SC_OK, ""); - // return; - // } - resp.sendError(HttpServletResponse.SC_NOT_FOUND); - } - - @Override - protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - // final String uri = req.getRequestURI(); - // final Matcher matcher = PROVIDERID_PATTERN.matcher(uri); - // if (matcher.find()) { - // final String id = matcher.group(1); - // this.providerStore.remove(id); - // sendResponse(resp, HttpServletResponse.SC_OK, ""); - // return; - // } - resp.sendError(HttpServletResponse.SC_NOT_FOUND); - } - private BearerToken doLogin(String username, String password, String domain) { if (!username.contains("@")) { username = String.format("%s@%s", username, domain); @@ -416,12 +432,6 @@ public class AuthHttpServlet extends HttpServlet { HttpServletRequest req = new HeadersOnlyHttpServletRequest( Map.of("Authorization", BaseHTTPClient.getAuthorizationHeaderValue(username, password))); if (this.odlAuthenticator.authenticate(req)) { - try { - LOG.info("userids={}", this.odlIdentityService.listUserIDs()); - LOG.info("domains={}", this.odlIdentityService.listDomains(username)); - } catch (IDMStoreException e) { - - } List roles = this.odlIdentityService.listRoles(username, domain); UserTokenPayload data = new UserTokenPayload(); data.setPreferredUsername(username); diff --git a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/AuthService.java b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/AuthService.java index ec685a003..3cb79757c 100644 --- a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/AuthService.java +++ b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/AuthService.java @@ -33,9 +33,11 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; +import java.util.stream.Collectors; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -52,16 +54,13 @@ import org.slf4j.LoggerFactory; public abstract class AuthService { - private static final Logger LOG = LoggerFactory.getLogger(AuthService.class.getName()); + private static final Logger LOG = LoggerFactory.getLogger(AuthService.class); private final MappingBaseHttpClient httpClient; protected final ObjectMapper mapper; protected final OAuthProviderConfig config; protected final TokenCreator tokenCreator; private final String redirectUri; - private String localHostUrl; - public void setLocalHostUrl(String url) { - this.localHostUrl = url; - } + protected abstract String getTokenVerifierUri(); protected abstract Map getAdditionalTokenVerifierParams(); @@ -75,23 +74,31 @@ public abstract class AuthService { protected abstract String getLoginUrl(String callbackUrl); + protected abstract UserTokenPayload requestUserRoles(String access_token, long expires_at); + + protected abstract boolean verifyState(String state); + public AuthService(OAuthProviderConfig config, String redirectUri, TokenCreator tokenCreator) { this.config = config; this.tokenCreator = tokenCreator; this.redirectUri = redirectUri; this.mapper = new ObjectMapper(); this.mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - this.httpClient = new MappingBaseHttpClient(this.config.getHost()); + this.httpClient = new MappingBaseHttpClient(this.config.getUrl()); } - public PublicOAuthProviderConfig getConfig(String host) { - return new PublicOAuthProviderConfig(this, host); + public PublicOAuthProviderConfig getConfig() { + return new PublicOAuthProviderConfig(this); } - public void handleRedirect(HttpServletRequest req, HttpServletResponse resp) throws IOException { + protected MappingBaseHttpClient getHttpClient() { + return this.httpClient; + } + + public void handleRedirect(HttpServletRequest req, HttpServletResponse resp, String host) throws IOException { switch (this.getResponseType()) { case CODE: - this.handleRedirectCode(req, resp); + this.handleRedirectCode(req, resp, host); break; case TOKEN: sendErrorResponse(resp, "not yet implemented"); @@ -99,39 +106,56 @@ public abstract class AuthService { case SESSION_STATE: break; } + } + public void sendLoginRedirectResponse(HttpServletResponse resp, String callbackUrl) { + resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); + resp.setHeader("Location", this.getLoginUrl(callbackUrl)); } private static void sendErrorResponse(HttpServletResponse resp, String message) throws IOException { resp.sendError(HttpServletResponse.SC_NOT_FOUND, message); - } - private void handleRedirectCode(HttpServletRequest req, HttpServletResponse resp) throws IOException { + private void handleRedirectCode(HttpServletRequest req, HttpServletResponse resp, String host) throws IOException { final String code = req.getParameter("code"); - OAuthResponseData response = this.getTokenForUser(code); + final String state = req.getParameter("state"); + OAuthResponseData response = null; + if(this.verifyState(state)) { + response = this.getTokenForUser(code, host); + } if (response != null) { if (this.doSeperateRolesRequest()) { - + //long expiresAt = this.tokenCreator.getDefaultExp(Math.round(response.getExpires_in())); + long expiresAt = this.tokenCreator.getDefaultExp(); + UserTokenPayload data = this.requestUserRoles(response.getAccess_token(), expiresAt); + if (data != null) { + this.handleUserInfoToken(data, resp, host); + } else { + sendErrorResponse(resp, "unable to verify user"); + } } else { - this.handleUserInfoToken(response.getAccess_token(), resp); + this.handleUserInfoToken(response.getAccess_token(), resp, host); } } else { sendErrorResponse(resp, "unable to verify code"); } + } + private void handleUserInfoToken(UserTokenPayload data, HttpServletResponse resp, String localHostUrl) + throws IOException { + BearerToken onapToken = this.tokenCreator.createNewJWT(data); + sendTokenResponse(resp, onapToken, localHostUrl); } - private void handleUserInfoToken(String accessToken, HttpServletResponse resp) throws IOException { + private void handleUserInfoToken(String accessToken, HttpServletResponse resp, String localHostUrl) + throws IOException { try { DecodedJWT jwt = JWT.decode(accessToken); - String spayload = base64Decode(jwt.getPayload()); - LOG.debug("payload in jwt from keycload='{}'", spayload); - + LOG.debug("payload in jwt='{}'", spayload); UserTokenPayload data = this.mapAccessToken(spayload); - BearerToken onapToken = this.tokenCreator.createNewJWT(data); - sendTokenResponse(resp, onapToken); + this.handleUserInfoToken(data, resp, localHostUrl); } catch (JWTDecodeException | JsonProcessingException e) { LOG.warn("unable to decode jwt token {}: ", accessToken, e); sendErrorResponse(resp, e.getMessage()); @@ -139,7 +163,12 @@ public abstract class AuthService { } - private void sendTokenResponse(HttpServletResponse resp, BearerToken data) throws IOException { + protected List mapRoles(List roles) { + final Map map = this.config.getRoleMapping(); + return roles.stream().map(r -> map.getOrDefault(r, r)).collect(Collectors.toList()); + } + + private void sendTokenResponse(HttpServletResponse resp, BearerToken data, String localHostUrl) throws IOException { if (this.redirectUri == null) { byte[] output = data != null ? mapper.writeValueAsString(data).getBytes() : new byte[0]; resp.setStatus(200); @@ -150,7 +179,7 @@ public abstract class AuthService { os.write(output); } else { resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); - resp.setHeader("Location", assembleUrl(this.localHostUrl, this.redirectUri, data.getToken())); + resp.setHeader("Location", assembleUrl(localHostUrl, this.redirectUri, data.getToken())); } } @@ -160,7 +189,7 @@ public abstract class AuthService { return new String(Base64.getDecoder().decode(data), StandardCharsets.UTF_8); } - private OAuthResponseData getTokenForUser(String code) { + private OAuthResponseData getTokenForUser(String code, String localHostUrl) { Map headers = new HashMap<>(); headers.put("Content-Type", "application/x-www-form-urlencoded"); @@ -168,15 +197,15 @@ public abstract class AuthService { params.put("code", code); params.put("client_id", this.config.getClientId()); params.put("client_secret", this.config.getSecret()); - params.put("redirect_uri", - assembleRedirectUrl(localHostUrl, AuthHttpServlet.REDIRECTURI, this.config.getId())); + params.put("redirect_uri", assembleRedirectUrl(localHostUrl, AuthHttpServlet.REDIRECTURI, this.config.getId())); StringBuilder body = new StringBuilder(); for (Entry p : params.entrySet()) { body.append(String.format("%s=%s&", p.getKey(), urlEncode(p.getValue()))); } - Optional> response = this.httpClient.sendMappedRequest(this.getTokenVerifierUri(), - "POST", body.substring(0, body.length() - 1), headers, OAuthResponseData.class); + Optional> response = + this.httpClient.sendMappedRequest(this.getTokenVerifierUri(), "POST", + body.substring(0, body.length() - 1), headers, OAuthResponseData.class); if (response.isPresent() && response.get().isSuccess()) { return response.get().body; } @@ -185,8 +214,6 @@ public abstract class AuthService { return null; } - - /** * Assemble callback url for service provider {host}{baseUri}/{serviceId} e.g. * http://10.20.0.11:8181/oauth/redirect/keycloak @@ -243,11 +270,10 @@ public abstract class AuthService { this.loginUrl = loginUrl; } - public PublicOAuthProviderConfig(AuthService authService, String host) { + public PublicOAuthProviderConfig(AuthService authService) { this.id = authService.config.getId(); this.title = authService.config.getTitle(); - this.loginUrl = authService.getLoginUrl( - assembleRedirectUrl(host, AuthHttpServlet.REDIRECTURI, this.id)); + this.loginUrl = String.format(AuthHttpServlet.LOGIN_REDIRECT_FORMAT, authService.config.getId()); } } diff --git a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/GitlabProviderService.java b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/GitlabProviderService.java new file mode 100644 index 000000000..4a8bdfa1b --- /dev/null +++ b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/GitlabProviderService.java @@ -0,0 +1,173 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : ccsdk features + * ================================================================================ + * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + */ +package org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.Config; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OAuthProviderConfig; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UserTokenPayload; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.http.client.MappedBaseHttpResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GitlabProviderService extends AuthService { + + private static final Logger LOG = LoggerFactory.getLogger(GitlabProviderService.class); + private Map additionalTokenVerifierParams; + protected final List randomIds; + private static final String API_USER_URI = "/api/v4/user"; + private static final String API_GROUP_URI = "/api/v4/groups?min_access_level=10"; + + public GitlabProviderService(OAuthProviderConfig config, String redirectUri, TokenCreator tokenCreator) { + super(config, redirectUri, tokenCreator); + this.additionalTokenVerifierParams = new HashMap<>(); + this.additionalTokenVerifierParams.put("grant_type", "authorization_code"); + this.randomIds = new ArrayList<>(); + } + + @Override + protected String getTokenVerifierUri() { + return "/oauth/token"; + } + + @Override + protected String getLoginUrl(String callbackUrl) { + return String.format("%s/oauth/authorize?client_id=%s&response_type=code&state=%s&redirect_uri=%s", + this.config.getUrl(), urlEncode(this.config.getClientId()), this.createRandomId(), callbackUrl); + } + + private String createRandomId() { + String rnd = null; + while(true) { + rnd=Config.generateSecret(20); + if(!this.randomIds.contains(rnd)) { + break; + } + } + this.randomIds.add(rnd); + return rnd; + } + + @Override + protected ResponseType getResponseType() { + return ResponseType.CODE; + } + + @Override + protected Map getAdditionalTokenVerifierParams() { + return this.additionalTokenVerifierParams; + + } + + @Override + protected boolean doSeperateRolesRequest() { + return true; + } + + @Override + protected UserTokenPayload mapAccessToken(String spayload) throws JsonMappingException, JsonProcessingException { + return null; + } + + @Override + protected UserTokenPayload requestUserRoles(String access_token, long expires_at) { + LOG.info("reqesting user roles with token={}", access_token); + Map authHeaders = new HashMap<>(); + authHeaders.put("Authorization", String.format("Bearer %s", access_token)); + Optional> userInfo = + this.getHttpClient().sendMappedRequest(API_USER_URI, "GET", null, authHeaders, GitlabUserInfo.class); + if (userInfo.isEmpty()) { + LOG.warn("unable to read user data"); + return null; + } + Optional> groupInfos = this.getHttpClient() + .sendMappedRequest(API_GROUP_URI, "GET", null, authHeaders, GitlabGroupInfo[].class); + if (groupInfos.isEmpty()) { + LOG.warn("unable to read group information for user"); + return null; + } + UserTokenPayload data = new UserTokenPayload(); + GitlabUserInfo uInfo = userInfo.get().body; + data.setPreferredUsername(uInfo.getUsername()); + data.setGivenName(uInfo.getName()); + data.setFamilyName(uInfo.getName()); + data.setExp(expires_at); + List roles = new ArrayList<>(); + GitlabGroupInfo[] uRoles = groupInfos.get().body; + for (GitlabGroupInfo uRole : uRoles) { + roles.add(uRole.getName()); + } + data.setRoles(this.mapRoles(roles)); + return data; + } + + + + @SuppressWarnings("unused") + private static class GitlabUserInfo { + + private String username; + private String name; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + @SuppressWarnings("unused") + private static class GitlabGroupInfo { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + @Override + protected boolean verifyState(String state) { + if(this.randomIds.contains(state)) { + this.randomIds.remove(state); + return true; + } + return false; + } +} diff --git a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/KeycloakProviderService.java b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/KeycloakProviderService.java index 3bfbb3b25..86383c983 100644 --- a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/KeycloakProviderService.java +++ b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/KeycloakProviderService.java @@ -51,17 +51,16 @@ public class KeycloakProviderService extends AuthService { protected String getLoginUrl(String callbackUrl) { return String.format( "%s/auth/realms/onap/protocol/openid-connect/auth?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", - this.config.getHost(), urlEncode(this.config.getClientId()), this.config.getScope(), + this.config.getUrl(), urlEncode(this.config.getClientId()), this.config.getScope(), urlEncode(callbackUrl)); } - - - private List mapRoles(List data) { - + @Override + protected List mapRoles(List data) { + final Map map = this.config.getRoleMapping(); List filteredRoles = data.stream().filter(role -> !role.equals("uma_authorization") && !role.equals("offline_access")) - .map(r -> r).collect(Collectors.toList()); + .map(r -> map.getOrDefault(r, r)).collect(Collectors.toList()); return filteredRoles; } @@ -93,5 +92,15 @@ public class KeycloakProviderService extends AuthService { return data; } + @Override + protected UserTokenPayload requestUserRoles(String access_token, long expires_at) { + return null; + } + + @Override + protected boolean verifyState(String state) { + return true; + } + } diff --git a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/MdSalAuthorizationStore.java b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/MdSalAuthorizationStore.java new file mode 100644 index 000000000..b181af040 --- /dev/null +++ b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/MdSalAuthorizationStore.java @@ -0,0 +1,106 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : ccsdk features + * ================================================================================ + * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + */ +package org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OdlPolicy; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OdlPolicy.PolicyMethods; +import org.opendaylight.mdsal.binding.api.DataBroker; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.HttpAuthorization; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.Policies; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.permission.Permissions; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.permission.Permissions.Actions; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MdSalAuthorizationStore { + + private static final Logger LOG = LoggerFactory.getLogger(MdSalAuthorizationStore.class.getName()); + + private final DataBroker dataBroker; + + public MdSalAuthorizationStore(DataBroker dataBroker) { + this.dataBroker = dataBroker; + } + + public Optional getPolicy(String path, List userRoles) { + InstanceIdentifier iif = InstanceIdentifier.create(HttpAuthorization.class).child(Policies.class); + Optional odata = Optional.empty(); + try { + odata = this.dataBroker.newReadOnlyTransaction().read(LogicalDatastoreType.CONFIGURATION, iif).get(); + } catch (InterruptedException | ExecutionException e) { + LOG.warn("unable to read policies from mdsal: ", e); + } + if (odata.isEmpty()) { + return Optional.empty(); + } + + Optional entry = + odata.get().getPolicies().stream().filter((e) -> path.equals(e.getResource())).findFirst(); + if (entry.isEmpty()) { + return Optional.empty(); + } + List permissions = entry.get().getPermissions(); + if (permissions == null) { + return Optional.empty(); + } + Optional rolePm = permissions.stream().filter((e) -> userRoles.contains(e.getRole())).findFirst(); + if (rolePm.isEmpty()) { + return Optional.empty(); + } + return Optional.of(mapPolicy(path, rolePm.get().getActions())); + } + + private OdlPolicy mapPolicy(String path, List actions) { + PolicyMethods methods = new PolicyMethods(); + String action; + for (Actions a : actions) { + action = a.getName().toLowerCase(); + switch (action) { + case "get": + methods.setGet(true); + break; + case "post": + methods.setPost(true); + break; + case "put": + methods.setPut(true); + break; + case "delete": + methods.setDelete(true); + break; + case "patch": + methods.setPatch(true); + break; + default: + LOG.warn("unknown http method {}",action); + break; + } + } + return new OdlPolicy(path, methods); + } + +} diff --git a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/NextcloudProviderService.java b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/NextcloudProviderService.java index ad1da68b3..03b0f4f75 100644 --- a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/NextcloudProviderService.java +++ b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/NextcloudProviderService.java @@ -70,4 +70,16 @@ public class NextcloudProviderService extends AuthService { return null; } + @Override + protected UserTokenPayload requestUserRoles(String access_token, long expires_at) { + // TODO Auto-generated method stub + return null; + } + + @Override + protected boolean verifyState(String state) { + // TODO Auto-generated method stub + return false; + } + } \ No newline at end of file diff --git a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/OAuthProviderFactory.java b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/OAuthProviderFactory.java index a09f150ef..193e7a7f7 100644 --- a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/OAuthProviderFactory.java +++ b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/OAuthProviderFactory.java @@ -33,12 +33,14 @@ public class OAuthProviderFactory { return new KeycloakProviderService(config, redirectUri, tokenCreator); case NEXTCLOUD: return new NextcloudProviderService(config, redirectUri, tokenCreator); + case GITLAB: + return new GitlabProviderService(config, redirectUri, tokenCreator); } return null; } public static enum OAuthProvider { - KEYCLOAK, NEXTCLOUD + KEYCLOAK, NEXTCLOUD, GITLAB } } diff --git a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/TokenCreator.java b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/TokenCreator.java index 3244f90e5..e7e9b72f9 100644 --- a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/TokenCreator.java +++ b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/TokenCreator.java @@ -41,28 +41,38 @@ public class TokenCreator { private static final Logger LOG = LoggerFactory.getLogger(AuthHttpServlet.class.getName()); private static final long DEFAULT_TOKEN_LIFETIME_MS = 30 * 60 * 1000; - private static final String TOKEN_ISSUER = Config.getProperty("${TOKEN_ISSUER}", "ONAP SDNC"); + private final String issuer; private static TokenCreator _instance; - private static final String SECRET = Config.getProperty("${TOKEN_SECRET}", "secret"); + private final String secret; private static final String ROLES_CLAIM = "roles"; private static final String FAMILYNAME_CLAIM = "family_name"; private static final String NAME_CLAIM = "name"; - public static TokenCreator getInstance() { + public static TokenCreator getInstance(Config config) { if (_instance == null) { - _instance = new TokenCreator(); + _instance = new TokenCreator(config); + } + return _instance; + } + public static TokenCreator getInstance(String secret, String issuer) { + if (_instance == null) { + _instance = new TokenCreator(secret, issuer); } return _instance; } - private TokenCreator() { - + private TokenCreator(Config config) { + this(config.getTokenSecret(),config.getTokenIssuer()); + } + private TokenCreator(String secret, String issuer) { + this.secret = secret; + this.issuer = issuer; } public BearerToken createNewJWT(UserTokenPayload data) { - Algorithm algorithm = Algorithm.HMAC256(SECRET); - final String token = JWT.create().withIssuer(TOKEN_ISSUER).withExpiresAt(new Date(data.getExp())) + Algorithm algorithm = Algorithm.HMAC256(secret); + final String token = JWT.create().withIssuer(issuer).withExpiresAt(new Date(data.getExp())) .withSubject(data.getPreferredUsername()).withClaim(NAME_CLAIM, data.getGivenName()) .withClaim(FAMILYNAME_CLAIM, data.getFamilyName()) .withArrayClaim(ROLES_CLAIM, data.getRoles().toArray(new String[data.getRoles().size()])) @@ -74,8 +84,8 @@ public class TokenCreator { DecodedJWT jwt = null; LOG.debug("try to verify token {}", token); try { - Algorithm algorithm = Algorithm.HMAC256(SECRET); - JWTVerifier verifier = JWT.require(algorithm).withIssuer(TOKEN_ISSUER).build(); + Algorithm algorithm = Algorithm.HMAC256(secret); + JWTVerifier verifier = JWT.require(algorithm).withIssuer(issuer).build(); jwt = verifier.verify(token); } catch (JWTVerificationException e) { @@ -88,6 +98,10 @@ public class TokenCreator { return new Date().getTime() + DEFAULT_TOKEN_LIFETIME_MS; } + public long getDefaultExp(long exp_in) { + return new Date().getTime() + exp_in; + } + public UserTokenPayload decode(HttpServletRequest req) throws JWTDecodeException { final String authHeader = req.getHeader("Authorization"); if (authHeader == null || !authHeader.startsWith("Bearer")) { -- cgit 1.2.3-korg