diff options
Diffstat (limited to 'sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers')
9 files changed, 1262 insertions, 0 deletions
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/AuthService.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/AuthService.java new file mode 100644 index 000000000..2dc0b5746 --- /dev/null +++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/AuthService.java @@ -0,0 +1,356 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : ccsdk features + * ================================================================================ + * Copyright (C) 2020 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.auth0.jwt.JWT; +import com.auth0.jwt.exceptions.JWTDecodeException; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +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; + +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.*; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.http.AuthHttpServlet; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.http.client.MappedBaseHttpResponse; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.http.client.MappingBaseHttpClient; +import org.apache.shiro.authc.BearerToken; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class AuthService { + + + 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 final String tokenEndpointRelative; + private final String authEndpointAbsolute; + private final String logoutEndpointAbsolute; + + private final Map<String, String> logoutTokenMap; + protected abstract String getTokenVerifierUri(); + + protected abstract Map<String, String> getAdditionalTokenVerifierParams(); + + protected abstract ResponseType getResponseType(); + + protected abstract boolean doSeperateRolesRequest(); + + protected abstract UserTokenPayload mapAccessToken(String spayload) + throws JsonMappingException, JsonProcessingException; + + protected abstract String getLoginUrl(String callbackUrl); + protected abstract String getLogoutUrl(); + + protected abstract UserTokenPayload requestUserRoles(String access_token, long issued_at, long expires_at); + + protected abstract boolean verifyState(String state); + + public AuthService(OAuthProviderConfig config, String redirectUri, TokenCreator tokenCreator) throws UnableToConfigureOAuthService { + 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.getUrlOrInternal(), this.config.trustAll()); + this.logoutTokenMap = new HashMap<>(); + if (this.config.hasToBeConfigured()){ + Optional<MappedBaseHttpResponse<OpenIdConfigResponseData>> oresponse = this.httpClient.sendMappedRequest( + this.config.getOpenIdConfigUrl(), "GET", null, null, OpenIdConfigResponseData.class); + if(oresponse.isEmpty()){ + throw new UnableToConfigureOAuthService(this.config.getOpenIdConfigUrl()); + } + MappedBaseHttpResponse<OpenIdConfigResponseData> response = oresponse.get(); + if(!response.isSuccess()){ + throw new UnableToConfigureOAuthService(this.config.getOpenIdConfigUrl(), response.code); + } + this.tokenEndpointRelative = trimUrl(this.config.getUrlOrInternal(),response.body.getToken_endpoint()); + this.authEndpointAbsolute = extendUrl(this.config.getUrlOrInternal(),response.body.getAuthorization_endpoint()); + this.logoutEndpointAbsolute = extendUrl(this.config.getUrlOrInternal(),response.body.getEnd_session_endpoint()); + } + else{ + this.tokenEndpointRelative = null; + this.authEndpointAbsolute = null; + this.logoutEndpointAbsolute = null; + } + } + + public static String trimUrl(String baseUrl, String endpoint) { + if(endpoint.startsWith(baseUrl)){ + return endpoint.substring(baseUrl.length()); + } + if(endpoint.startsWith("http")){ + return endpoint.substring(endpoint.indexOf("/",8)); + } + return endpoint; + } + public static String extendUrl(String baseUrl, String endpoint) { + if(endpoint.startsWith("http")){ + endpoint= endpoint.substring(endpoint.indexOf("/",8)); + } + if(baseUrl.endsWith("/")){ + baseUrl=baseUrl.substring(0,baseUrl.length()-2); + } + return baseUrl+endpoint; + } + + public PublicOAuthProviderConfig getConfig() { + return new PublicOAuthProviderConfig(this); + } + + 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, host); + break; + case TOKEN: + sendErrorResponse(resp, "not yet implemented"); + break; + case SESSION_STATE: + break; + } + } + + public void sendLoginRedirectResponse(HttpServletResponse resp, String callbackUrl) { + resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); + String url = this.authEndpointAbsolute !=null?String.format( + "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", + this.authEndpointAbsolute, urlEncode(this.config.getClientId()), this.config.getScope(), + urlEncode(callbackUrl)):this.getLoginUrl(callbackUrl); + resp.setHeader("Location", url); + } + public void sendLogoutRedirectResponse(String token, HttpServletResponse resp, String redirectUrl) + throws IOException { + String idToken = this.logoutTokenMap.getOrDefault(token, null); + String logoutEndpoint = this.logoutEndpointAbsolute!=null?this.logoutEndpointAbsolute:this.getLogoutUrl(); + if(idToken==null) { + LOG.debug("unable to find token in map. Do unsafe logout."); + resp.sendRedirect(this.logoutEndpointAbsolute); + return; + } + LOG.debug("id token found. redirect to specific logout"); + resp.sendRedirect(String.format("%s?id_token_hint=%s&post_logout_redirect_uri=%s",logoutEndpoint, idToken, + urlEncode(redirectUrl))); + } + + + + private static void sendErrorResponse(HttpServletResponse resp, String message) throws IOException { + resp.sendError(HttpServletResponse.SC_NOT_FOUND, message); + } + + private void handleRedirectCode(HttpServletRequest req, HttpServletResponse resp, String host) throws IOException { + final String code = req.getParameter("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()) { + LOG.debug("do a seperate role request"); + long expiresAt = this.tokenCreator.getDefaultExp(); + long issuedAt = this.tokenCreator.getDefaultIat(); + UserTokenPayload data = this.requestUserRoles(response.getAccess_token(), issuedAt, expiresAt); + if (data != null) { + BearerToken createdToken = this.handleUserInfoToken(data, resp, host); + this.logoutTokenMap.put(createdToken.getToken(),response.getId_token()); + } else { + sendErrorResponse(resp, "unable to verify user"); + } + } else { + BearerToken createdToken = this.handleUserInfoToken(response.getAccess_token(), resp, host); + this.logoutTokenMap.put(createdToken.getToken(),response.getId_token()); + } + } else { + sendErrorResponse(resp, "unable to verify code"); + } + } + + private BearerToken handleUserInfoToken(UserTokenPayload data, HttpServletResponse resp, String localHostUrl) + throws IOException { + BearerToken onapToken = this.tokenCreator.createNewJWT(data); + sendTokenResponse(resp, onapToken, localHostUrl); + return onapToken; + } + + private BearerToken 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='{}'", spayload); + UserTokenPayload data = this.mapAccessToken(spayload); + return this.handleUserInfoToken(data, resp, localHostUrl); + } catch (JWTDecodeException | JsonProcessingException e) { + LOG.warn("unable to decode jwt token {}: ", accessToken, e); + sendErrorResponse(resp, e.getMessage()); + } + return null; + } + + + protected List<String> mapRoles(List<String> roles) { + final Map<String, String> 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); + resp.setContentLength(output.length); + resp.setContentType("application/json"); + resp.addCookie(this.tokenCreator.createAuthCookie(data)); + ServletOutputStream os = null; + os = resp.getOutputStream(); + os.write(output); + } else { + resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); + resp.setHeader("Location", assembleUrl(localHostUrl, this.redirectUri, data.getToken())); + resp.addCookie(this.tokenCreator.createAuthCookie(data)); + } + } + + + + private static String base64Decode(String data) { + return new String(Base64.getDecoder().decode(data), StandardCharsets.UTF_8); + } + + private OAuthResponseData getTokenForUser(String code, String localHostUrl) { + + Map<String, String> headers = new HashMap<>(); + headers.put("Content-Type", "application/x-www-form-urlencoded"); + headers.put("Accept", "application/json"); + Map<String, String> params = this.getAdditionalTokenVerifierParams(); + 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())); + StringBuilder body = new StringBuilder(); + for (Entry<String, String> p : params.entrySet()) { + body.append(String.format("%s=%s&", p.getKey(), urlEncode(p.getValue()))); + } + + String url = this.tokenEndpointRelative !=null?this.tokenEndpointRelative :this.getTokenVerifierUri(); + Optional<MappedBaseHttpResponse<OAuthResponseData>> response = + this.httpClient.sendMappedRequest(url, "POST", + body.substring(0, body.length() - 1), headers, OAuthResponseData.class); + if (response.isPresent() && response.get().isSuccess()) { + return response.get().body; + } + LOG.warn("problem get token for code {}", code); + + return null; + } + + /** + * Assemble callback url for service provider {host}{baseUri}/{serviceId} e.g. + * http://10.20.0.11:8181/oauth/redirect/keycloak + * + * @param host + * @param baseUri + * @param serviceId + * @return + */ + public static String assembleRedirectUrl(String host, String baseUri, String serviceId) { + return String.format("%s%s/%s", host, baseUri, serviceId); + } + + private static String assembleUrl(String host, String uri, String token) { + return String.format("%s%s%s", host, uri, token); + } + + public static String urlEncode(String s) { + return URLEncoder.encode(s, StandardCharsets.UTF_8); + } + + + + public enum ResponseType { + CODE, TOKEN, SESSION_STATE + } + + + public static class PublicOAuthProviderConfig { + + private String id; + private String title; + private String loginUrl; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getLoginUrl() { + return loginUrl; + } + + public void setLoginUrl(String loginUrl) { + this.loginUrl = loginUrl; + } + + public PublicOAuthProviderConfig(AuthService authService) { + this.id = authService.config.getId(); + this.title = authService.config.getTitle(); + this.loginUrl = String.format(AuthHttpServlet.LOGIN_REDIRECT_FORMAT, authService.config.getId()); + } + + } + + + +} diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/GitlabProviderService.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/GitlabProviderService.java new file mode 100644 index 000000000..d271948c2 --- /dev/null +++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/GitlabProviderService.java @@ -0,0 +1,180 @@ +/* + * ============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.UnableToConfigureOAuthService; +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<String, String> additionalTokenVerifierParams; + protected final List<String> 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) throws UnableToConfigureOAuthService { + 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); + } + + @Override + protected String getLogoutUrl() { + return String.format("%s/oauth/logout", this.config.getUrl()); + } + + 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<String, String> 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 issued_at, long expires_at) { + LOG.info("reqesting user roles with token={}", access_token); + Map<String, String> authHeaders = new HashMap<>(); + authHeaders.put("Authorization", String.format("Bearer %s", access_token)); + Optional<MappedBaseHttpResponse<GitlabUserInfo>> 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<MappedBaseHttpResponse<GitlabGroupInfo[]>> 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.setIat(issued_at); + data.setExp(expires_at); + List<String> 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/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/KeycloakProviderService.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/KeycloakProviderService.java new file mode 100644 index 000000000..bdbf9286a --- /dev/null +++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/KeycloakProviderService.java @@ -0,0 +1,115 @@ +/* + * ============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.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.KeycloakUserTokenPayload; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OAuthProviderConfig; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UnableToConfigureOAuthService; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UserTokenPayload; + +public class KeycloakProviderService extends AuthService { + + public static final String ID = "keycloak"; + private Map<String, String> additionalTokenVerifierParams; + + public KeycloakProviderService(OAuthProviderConfig config, String redirectUri, TokenCreator tokenCreator) throws UnableToConfigureOAuthService { + super(config, redirectUri, tokenCreator); + this.additionalTokenVerifierParams = new HashMap<>(); + this.additionalTokenVerifierParams.put("grant_type", "authorization_code"); + } + + @Override + protected String getTokenVerifierUri() { + return String.format("/auth/realms/%s/protocol/openid-connect/token", urlEncode(this.config.getRealmName())); + } + + @Override + protected String getLoginUrl(String callbackUrl) { + return String.format( + "%s/auth/realms/%s/protocol/openid-connect/auth?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", + this.config.getUrl(), urlEncode(this.config.getRealmName()), urlEncode(this.config.getClientId()), + this.config.getScope(), urlEncode(callbackUrl)); + } + + @Override + protected String getLogoutUrl() { + return String.format("%s/auth/realms/%s/protocol/openid-connect/logout", this.config.getUrl(), + urlEncode(this.config.getRealmName())); + } + + @Override + protected List<String> mapRoles(List<String> data) { + final Map<String, String> map = this.config.getRoleMapping(); + List<String> filteredRoles = + data.stream().filter(role -> !role.equals("uma_authorization") && !role.equals("offline_access")) + .map(r -> map.getOrDefault(r, r)).collect(Collectors.toList()); + return filteredRoles; + } + + @Override + protected ResponseType getResponseType() { + return ResponseType.CODE; + } + + @Override + protected Map<String, String> getAdditionalTokenVerifierParams() { + return this.additionalTokenVerifierParams; + + } + + @Override + protected boolean doSeperateRolesRequest() { + return false; + } + + @Override + protected UserTokenPayload mapAccessToken(String spayload) throws JsonMappingException, JsonProcessingException { + KeycloakUserTokenPayload payload = mapper.readValue(spayload, KeycloakUserTokenPayload.class); + UserTokenPayload data = new UserTokenPayload(); + data.setIat(payload.getIat() * 1000L); + data.setExp(payload.getExp() * 1000L); + data.setFamilyName(payload.getFamilyName()); + data.setGivenName(payload.getGivenName()); + data.setProviderId(this.config.getId()); + data.setPreferredUsername(payload.getPreferredUsername()); + data.setRoles(this.mapRoles(payload.getRealmAccess().getRoles())); + return data; + } + + @Override + protected UserTokenPayload requestUserRoles(String access_token, long issued_at, long expires_at) { + return null; + } + + @Override + protected boolean verifyState(String state) { + return true; + } + + +} diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/MdSalAuthorizationStore.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/MdSalAuthorizationStore.java new file mode 100644 index 000000000..4bf35e72d --- /dev/null +++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/MdSalAuthorizationStore.java @@ -0,0 +1,118 @@ +/* + * ============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.Set; +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.binding.api.ReadTransaction; +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<OdlPolicy> getPolicy(String path, List<String> userRoles) { + InstanceIdentifier<Policies> iif = InstanceIdentifier.create(HttpAuthorization.class).child(Policies.class); + Optional<Policies> odata = Optional.empty(); + // The implicite close is not handled correctly by underlaying opendaylight netconf service + ReadTransaction transaction = this.dataBroker.newReadOnlyTransaction(); + try { + odata = transaction.read(LogicalDatastoreType.CONFIGURATION, iif).get(); + } catch (ExecutionException e) { + LOG.warn("unable to read policies from mdsal: ", e); + } catch (InterruptedException e) { + LOG.warn("Interrupted!", e); + // Restore interrupted state... + Thread.currentThread().interrupt(); + } + if (odata.isEmpty()) { + return Optional.empty(); + } + List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies> data = + odata.get().getPolicies(); + if (data == null) { + return Optional.empty(); + } + Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies> entry = + data.stream().filter(e -> path.equals(e.getResource())).findFirst(); + if (entry.isEmpty()) { + return Optional.empty(); + } + List<Permissions> permissions = entry.get().getPermissions(); + if (permissions == null) { + return Optional.empty(); + } + Optional<Permissions> 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, Set<Actions> 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/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/NextcloudProviderService.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/NextcloudProviderService.java new file mode 100644 index 000000000..73bae5d4c --- /dev/null +++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/NextcloudProviderService.java @@ -0,0 +1,91 @@ +/* + * ============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.Map; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OAuthProviderConfig; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UnableToConfigureOAuthService; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UserTokenPayload; + +public class NextcloudProviderService extends AuthService { + + public NextcloudProviderService(OAuthProviderConfig config, String redirectUri, TokenCreator tokenCreator) throws UnableToConfigureOAuthService { + super(config, redirectUri, tokenCreator); + // TODO Auto-generated constructor stub + } + + @Override + protected String getTokenVerifierUri() { + // TODO Auto-generated method stub + return null; + } + + @Override + protected Map<String, String> getAdditionalTokenVerifierParams() { + // TODO Auto-generated method stub + return null; + } + + @Override + protected ResponseType getResponseType() { + // TODO Auto-generated method stub + return ResponseType.TOKEN; + } + + @Override + protected boolean doSeperateRolesRequest() { + // TODO Auto-generated method stub + return false; + } + + @Override + protected UserTokenPayload mapAccessToken(String spayload) throws JsonMappingException, JsonProcessingException { + // TODO Auto-generated method stub + return null; + } + + @Override + protected String getLoginUrl(String callbackUrl) { + // TODO Auto-generated method stub + return null; + } + + @Override + protected String getLogoutUrl() { + return null; + } + + @Override + protected UserTokenPayload requestUserRoles(String access_token, long issued_at, 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/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/OAuthProviderFactory.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/OAuthProviderFactory.java new file mode 100644 index 000000000..152569930 --- /dev/null +++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/OAuthProviderFactory.java @@ -0,0 +1,47 @@ +/* + * ============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 org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OAuthProviderConfig; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UnableToConfigureOAuthService; + +public class OAuthProviderFactory { + + + public static AuthService create(OAuthProvider key, OAuthProviderConfig config, String redirectUri, + TokenCreator tokenCreator) throws UnableToConfigureOAuthService { + switch (key) { + case KEYCLOAK: + 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, GITLAB + } + +} diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/PemUtils.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/PemUtils.java new file mode 100644 index 000000000..fac46f6b1 --- /dev/null +++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/PemUtils.java @@ -0,0 +1,106 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : ccsdk features + * ================================================================================ + * Copyright (C) 2020 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.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.EncodedKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemReader; + +public class PemUtils { + + private static byte[] parsePEMFile(File pemFile) throws IOException { + if (!pemFile.isFile() || !pemFile.exists()) { + throw new FileNotFoundException(String.format("The file '%s' doesn't exist.", pemFile.getAbsolutePath())); + } + return parsePEMFile(new FileReader(pemFile)); + } + private static byte[] parsePEMFile(Reader inputReader) throws IOException { + PemReader reader = new PemReader(inputReader); + PemObject pemObject = reader.readPemObject(); + byte[] content = pemObject.getContent(); + reader.close(); + return content; + } + private static PublicKey getPublicKey(byte[] keyBytes, String algorithm) { + PublicKey publicKey = null; + try { + KeyFactory kf = KeyFactory.getInstance(algorithm); + EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); + publicKey = kf.generatePublic(keySpec); + } catch (NoSuchAlgorithmException e) { + System.out.println("Could not reconstruct the public key, the given algorithm could not be found."); + } catch (InvalidKeySpecException e) { + System.out.println("Could not reconstruct the public key"); + } + + return publicKey; + } + + private static PrivateKey getPrivateKey(byte[] keyBytes, String algorithm) { + PrivateKey privateKey = null; + try { + KeyFactory kf = KeyFactory.getInstance(algorithm); + EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + privateKey = kf.generatePrivate(keySpec); + } catch (NoSuchAlgorithmException e) { + System.out.println("Could not reconstruct the private key, the given algorithm could not be found."); + } catch (InvalidKeySpecException e) { + System.out.println("Could not reconstruct the private key"); + } + + return privateKey; + } + + public static PublicKey readPublicKeyFromFile(String filepath, String algorithm) throws IOException { + byte[] bytes = PemUtils.parsePEMFile(new File(filepath)); + return PemUtils.getPublicKey(bytes, algorithm); + } + + public static PublicKey readPublicKey(String filecontent, String algorithm) throws IOException { + byte[] bytes = PemUtils.parsePEMFile(new StringReader(filecontent)); + return PemUtils.getPublicKey(bytes, algorithm); + } + + public static PrivateKey readPrivateKeyFromFile(String filepath, String algorithm) throws IOException { + byte[] bytes = PemUtils.parsePEMFile(new File(filepath)); + return PemUtils.getPrivateKey(bytes, algorithm); + } + + public static PrivateKey readPrivateKey(String filecontent, String algorithm) throws IOException { + byte[] bytes = PemUtils.parsePEMFile(new StringReader(filecontent)); + return PemUtils.getPrivateKey(bytes, algorithm); + } + +}
\ No newline at end of file diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/RSAKeyReader.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/RSAKeyReader.java new file mode 100644 index 000000000..028dff9dd --- /dev/null +++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/RSAKeyReader.java @@ -0,0 +1,47 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : ccsdk features + * ================================================================================ + * Copyright (C) 2020 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.io.IOException; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; + +public class RSAKeyReader { + + private static final String PREFIX_FILEURL = "file://"; + + public static RSAPrivateKey getPrivateKey(String filenameOrContent) throws IOException { + if (filenameOrContent.startsWith(PREFIX_FILEURL)) { + return (RSAPrivateKey) PemUtils.readPrivateKeyFromFile(filenameOrContent.substring(PREFIX_FILEURL.length()), + "RSA"); + } + return (RSAPrivateKey) PemUtils.readPrivateKey(filenameOrContent, "RSA"); + } + + public static RSAPublicKey getPublicKey(String filenameOrContent) throws IOException { + if (filenameOrContent.startsWith(PREFIX_FILEURL)) { + return (RSAPublicKey) PemUtils.readPublicKeyFromFile(filenameOrContent.substring(PREFIX_FILEURL.length()), + "RSA"); + } + return (RSAPublicKey) PemUtils.readPublicKey(filenameOrContent, "RSA"); + } +} diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/TokenCreator.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/TokenCreator.java new file mode 100644 index 000000000..d8720e823 --- /dev/null +++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/TokenCreator.java @@ -0,0 +1,202 @@ +/* + * ============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.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTDecodeException; +import com.auth0.jwt.exceptions.JWTVerificationException; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.auth0.jwt.interfaces.JWTVerifier; +import java.io.IOException; +import java.security.Security; +import java.util.Arrays; +import java.util.Date; +import java.util.Optional; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.Config; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UserTokenPayload; +import org.onap.ccsdk.features.sdnr.wt.oauthprovider.http.AuthHttpServlet; +import org.apache.shiro.authc.BearerToken; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TokenCreator { + + private static final Logger LOG = LoggerFactory.getLogger(AuthHttpServlet.class.getName()); + private final String issuer; + private static TokenCreator _instance; + private final long tokenLifetimeSeconds; + private final Algorithm algorithm; + + private static final String ROLES_CLAIM = "roles"; + private static final String FAMILYNAME_CLAIM = "family_name"; + private static final String NAME_CLAIM = "name"; + private static final String PROVIDERID_CLAIM = "provider_id"; + private static final String COOKIE_NAME_AUTH = "token"; + + static { + Security.addProvider( + new BouncyCastleProvider() + ); + } + public static TokenCreator getInstance(Config config) throws IllegalArgumentException, IOException { + if (_instance == null) { + _instance = new TokenCreator(config); + } + return _instance; + } + + public static TokenCreator getInstance(String alg, String secret, String issuer, long tokenLifetime) + throws IllegalArgumentException, IOException { + return getInstance(alg, secret, null, issuer, tokenLifetime); + } + + public static TokenCreator getInstance(String alg, String secret, String pubkey, String issuer, long tokenLifetime) + throws IllegalArgumentException, IOException { + if (_instance == null) { + _instance = new TokenCreator(alg, secret, pubkey, issuer, tokenLifetime); + } + return _instance; + } + + private TokenCreator(Config config) throws IllegalArgumentException, IOException { + this(config.getAlgorithm(), config.getTokenSecret(), config.getPublicKey(), config.getTokenIssuer(), + config.getTokenLifetime()); + } + + private TokenCreator(String alg, String secret, String pubkey, String issuer, long tokenLifetime) + throws IllegalArgumentException, IOException { + this.issuer = issuer; + this.tokenLifetimeSeconds = tokenLifetime; + this.algorithm = this.createAlgorithm(alg, secret, pubkey); + } + + private Algorithm createAlgorithm(String alg, String secret, String pubkey) + throws IllegalArgumentException, IOException { + if (alg == null) { + alg = Config.TOKENALG_HS256; + } + switch (alg) { + case Config.TOKENALG_HS256: + return Algorithm.HMAC256(secret); + case Config.TOKENALG_RS256: + return Algorithm.RSA256(RSAKeyReader.getPublicKey(pubkey), RSAKeyReader.getPrivateKey(secret)); + case Config.TOKENALG_RS512: + return Algorithm.RSA512(RSAKeyReader.getPublicKey(pubkey), RSAKeyReader.getPrivateKey(secret)); + case Config.TOKENALG_CLIENT_RS256: + return Algorithm.RSA256(RSAKeyReader.getPublicKey(pubkey), null); + case Config.TOKENALG_CLIENT_RS512: + return Algorithm.RSA512(RSAKeyReader.getPublicKey(pubkey), null); + } + throw new IllegalArgumentException(String.format("unable to find algorithm for %s", alg)); + + } + + public BearerToken createNewJWT(UserTokenPayload data) { + final String token = JWT.create().withIssuer(issuer).withExpiresAt(new Date(data.getExp())) + .withIssuedAt(new Date(data.getIat())).withSubject(data.getPreferredUsername()) + .withClaim(NAME_CLAIM, data.getGivenName()).withClaim(FAMILYNAME_CLAIM, data.getFamilyName()) + .withClaim(PROVIDERID_CLAIM, data.getProviderId()) + .withArrayClaim(ROLES_CLAIM, data.getRoles().toArray(new String[data.getRoles().size()])) + .sign(this.algorithm); + LOG.trace("token created: {}", token); + return new BearerToken(token); + } + + public DecodedJWT verify(String token) { + DecodedJWT jwt = null; + LOG.debug("try to verify token {}", token); + try { + JWTVerifier verifier = JWT.require(this.algorithm).withIssuer(issuer).build(); + jwt = verifier.verify(token); + + } catch (JWTVerificationException e) { + LOG.warn("unable to verify token {}:", token, e); + } + return jwt; + } + + public long getDefaultExp() { + return new Date().getTime() + (this.tokenLifetimeSeconds * 1000); + } + + public long getDefaultExp(long expIn) { + return new Date().getTime() + expIn; + } + + public long getDefaultIat() { + return new Date().getTime(); + } + + public String getBearerToken(HttpServletRequest req) { + return this.getBearerToken(req, false); + } + + public String getBearerToken(HttpServletRequest req, boolean checkCookie) { + final String authHeader = req.getHeader("Authorization"); + if ((authHeader == null || !authHeader.startsWith("Bearer")) && checkCookie) { + Cookie[] cookies = req.getCookies(); + Optional<Cookie> ocookie = Optional.empty(); + if (cookies != null) { + ocookie = Arrays.stream(cookies).filter(c -> c != null && COOKIE_NAME_AUTH.equals(c.getName())) + .findFirst(); + } + if (ocookie.isEmpty()) { + return null; + } + return ocookie.get().getValue(); + } + return authHeader.substring(7); + } + + public UserTokenPayload decode(HttpServletRequest req) throws JWTDecodeException { + final String token = this.getBearerToken(req); + return token != null ? this.decode(token) : null; + } + + public UserTokenPayload decode(String token) { + if (token == null) { + return null; + } + DecodedJWT jwt = JWT.decode(token); + UserTokenPayload data = new UserTokenPayload(); + data.setRoles(Arrays.asList(jwt.getClaim(ROLES_CLAIM).asArray(String.class))); + data.setExp(jwt.getExpiresAt().getTime()); + data.setFamilyName(jwt.getClaim(FAMILYNAME_CLAIM).asString()); + data.setGivenName(jwt.getClaim(NAME_CLAIM).asString()); + data.setPreferredUsername(jwt.getClaim(NAME_CLAIM).asString()); + data.setProviderId(jwt.getClaim(PROVIDERID_CLAIM).asString()); + return data; + } + + public Cookie createAuthCookie(BearerToken data) { + Cookie cookie = new Cookie(COOKIE_NAME_AUTH, data.getToken()); + cookie.setMaxAge((int) this.tokenLifetimeSeconds); + cookie.setPath("/"); + cookie.setHttpOnly(true); + cookie.setSecure(true); + return cookie; + } +} |