diff options
author | Michael Dürre <michael.duerre@highstreet-technologies.com> | 2022-07-29 10:09:11 +0200 |
---|---|---|
committer | Michael Dürre <michael.duerre@highstreet-technologies.com> | 2022-07-29 10:09:22 +0200 |
commit | 47af0edbd755498f359ce252bf121df211f74523 (patch) | |
tree | 24ff4ef345d22c0718f8563007c511dfa01319cc /sdnr/wt | |
parent | 25423c50e504676f15c7a57c03aad40bfc35c7e6 (diff) |
update oauth-provider
increase support and add logout and filters
Issue-ID: CCSDK-3733
Change-Id: Id1f64a60e5e3b0b63a025281a97190ccc4065f47
Signed-off-by: Michael Dürre <michael.duerre@highstreet-technologies.com>
Diffstat (limited to 'sdnr/wt')
12 files changed, 227 insertions, 31 deletions
diff --git a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthResponseData.java b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthResponseData.java index 806a62062..0e25b5b0f 100644 --- a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthResponseData.java +++ b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthResponseData.java @@ -28,6 +28,7 @@ public class OAuthResponseData { private double refresh_expires_in; private String refresh_token; private String token_type; + private String id_token; public OAuthResponseData() { } @@ -76,6 +77,8 @@ public class OAuthResponseData { this.access_token = access_token; } + public void setId_token(String id_token){ this.id_token = id_token;} + public String getId_token(){ return this.id_token;} @Override public String toString() { return "OAuthResponseData [access_token=" + access_token + ", expires_in=" + expires_in diff --git a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OpenIdConfigResponseData.java b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OpenIdConfigResponseData.java index 2af46b6b4..d94631fe3 100644 --- a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OpenIdConfigResponseData.java +++ b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OpenIdConfigResponseData.java @@ -6,6 +6,8 @@ public class OpenIdConfigResponseData { private String authorization_endpoint; private String token_endpoint; private String userinfo_endpoint; + + private String end_session_endpoint; private String jwks_uri; public OpenIdConfigResponseData(){ @@ -51,4 +53,13 @@ public class OpenIdConfigResponseData { public void setJwks_uri(String jwks_uri) { this.jwks_uri = jwks_uri; } + + public String getEnd_session_endpoint() { + return end_session_endpoint; + } + + public void setEnd_session_endpoint(String end_session_endpoint) { + this.end_session_endpoint = end_session_endpoint; + } + } diff --git a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/UserTokenPayload.java b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/UserTokenPayload.java index a983dd69f..f7731f0b8 100644 --- a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/UserTokenPayload.java +++ b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/UserTokenPayload.java @@ -25,6 +25,8 @@ import java.util.List; public class UserTokenPayload { + public static final String PROVIDERID_INTERNAL="Internal"; + private List<String> roles; private String preferredUsername; private String givenName; @@ -32,6 +34,7 @@ public class UserTokenPayload { private long exp; private long iat; + private String providerId; public long getExp() { return exp; @@ -81,12 +84,20 @@ public class UserTokenPayload { this.roles = roles; } - public static UserTokenPayload create(String username, List<String> roles) { + public void setProviderId(String providerId){ this.providerId = providerId;} + + public String getProviderId(){ return this.providerId;} + + public static UserTokenPayload createInternal(String username, List<String> roles) { UserTokenPayload data = new UserTokenPayload(); data.setPreferredUsername(username); data.setRoles(roles); + data.setProviderId(PROVIDERID_INTERNAL); return data; } + public boolean isInternal() { + return PROVIDERID_INTERNAL.equals(this.providerId); + } } diff --git a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/filters/CustomizedMDSALDynamicAuthorizationFilter.java b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/filters/CustomizedMDSALDynamicAuthorizationFilter.java index 80d9d1bb6..b2523171d 100644 --- a/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/filters/CustomizedMDSALDynamicAuthorizationFilter.java +++ b/sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/filters/CustomizedMDSALDynamicAuthorizationFilter.java @@ -7,12 +7,15 @@ import com.google.common.collect.Iterables; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import java.io.IOException; import java.util.*; import java.util.concurrent.ExecutionException; import javax.servlet.Filter; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.authz.AuthorizationFilter; import org.opendaylight.aaa.shiro.web.env.ThreadLocals; @@ -78,6 +81,8 @@ public class CustomizedMDSALDynamicAuthorizationFilter extends AuthorizationFilt final Object mappedValue) { checkArgument(request instanceof HttpServletRequest, "Expected HttpServletRequest, received {}", request); + + final boolean defaultReturnValue=false; final Subject subject = getSubject(request, response); final HttpServletRequest httpServletRequest = (HttpServletRequest)request; final String requestURI = httpServletRequest.getRequestURI(); @@ -96,7 +101,7 @@ public class CustomizedMDSALDynamicAuthorizationFilter extends AuthorizationFilt // The authorization container does not exist-- hence no authz rules are present // Allow access. LOG.debug("Authorization Container does not exist"); - return true; + return defaultReturnValue; } final HttpAuthorization httpAuthorization = authorizationOptional.get(); @@ -104,8 +109,9 @@ public class CustomizedMDSALDynamicAuthorizationFilter extends AuthorizationFilt List<Policies> policiesList = policies != null ? policies.getPolicies() : null; if (policiesList == null || policiesList.isEmpty()) { // The authorization container exists, but no rules are present. Allow access. - LOG.debug("Exiting successfully early since no authorization rules exist"); - return true; + LOG.debug("Exiting early since no authorization rules exist"); + sendError(response, 403, ""); + return defaultReturnValue; } // Sort the Policies list based on index @@ -116,10 +122,11 @@ public class CustomizedMDSALDynamicAuthorizationFilter extends AuthorizationFilt final String resource = policy.getResource(); final boolean pathsMatch = pathsMatch(resource, requestURI); if (pathsMatch) { - LOG.debug("paths match for pattern={} and requestURI={}", resource, requestURI); + LOG.debug("paths match for policy {} pattern={} and requestURI={}", policy.getIndex(), resource, requestURI); final String method = httpServletRequest.getMethod(); LOG.trace("method={}", method); List<Permissions> permissions = policy.getPermissions(); + LOG.trace("perm={}", permissions); if(permissions !=null) { for (Permissions permission : permissions) { final String role = permission.getRole(); @@ -146,10 +153,25 @@ public class CustomizedMDSALDynamicAuthorizationFilter extends AuthorizationFilt LOG.trace("no permissions found"); } LOG.debug("couldn't authorize the user for access"); + sendError(response, 403, ""); return false; } } - LOG.debug("successfully authorized the user for access"); - return true; + LOG.debug("no path found that matches {}", requestURI); + sendError(response, 403, ""); + return defaultReturnValue; + } + + private void sendError(ServletResponse response, int code, String message) { + if(response instanceof HttpServletResponse){ + try { + ((HttpServletResponse)response).sendError(code, message); + } catch (IOException e) { + LOG.warn("unable to send {} {} response: ", code, message, e); + } + } + else{ + LOG.warn("unable to send {} {} response", code, message); + } } } 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 7c88e50b0..7953f31e6 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 @@ -78,6 +78,7 @@ public class AuthHttpServlet extends HttpServlet { private static final String DEFAULT_DOMAIN = "sdn"; private static final String HEAEDER_AUTHORIZATION = "Authorization"; + private static final String LOGOUT_REDIRECT_URL_PARAMETER = "redirect_uri"; private static final String CLASSNAME_ODLBASICAUTH = "org.opendaylight.aaa.shiro.filters.ODLHttpAuthenticationFilter"; private static final String CLASSNAME_ODLBEARERANDBASICAUTH = @@ -146,8 +147,21 @@ public class AuthHttpServlet extends HttpServlet { } private void handleLogout(HttpServletRequest req, HttpServletResponse resp) throws IOException { + final String bearerToken = this.tokenCreator.getBearerToken(req, true); + UserTokenPayload userInfo = this.tokenCreator.decode(bearerToken); + if (bearerToken != null && userInfo!=null && !userInfo.isInternal()) { + AuthService provider = this.providerStore.getOrDefault(userInfo.getProviderId(), null); + if (provider != null) { + String redirectUrl = req.getParameter(LOGOUT_REDIRECT_URL_PARAMETER); + if (redirectUrl == null) { + redirectUrl = this.config.getPublicUrl(); + } + provider.sendLogoutRedirectResponse(bearerToken, resp, redirectUrl); + return; + } + } this.logout(); - this.sendResponse(resp, HttpServletResponse.SC_OK, ""); + this.sendResponse(resp, HttpServletResponse.SC_OK); } private void handleLoginRedirect(HttpServletRequest req, HttpServletResponse resp) throws IOException { @@ -308,7 +322,7 @@ public class AuthHttpServlet extends HttpServlet { username = String.format("%s@%s", username, domain); } List<String> roles = odlIdentityService.listRoles(username, domain); - return UserTokenPayload.create(username, roles); + return UserTokenPayload.createInternal(username, roles); } } return null; @@ -449,7 +463,9 @@ public class AuthHttpServlet extends HttpServlet { } - + private void sendResponse(HttpServletResponse resp, int code) throws IOException { + this.sendResponse(resp, code, null); + } private void sendResponse(HttpServletResponse resp, int code, Object data) throws IOException { byte[] output = data != null ? mapper.writeValueAsString(data).getBytes() : new byte[0]; // output 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 192da6371..2dc0b5746 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 @@ -59,9 +59,11 @@ public abstract class AuthService { protected final OAuthProviderConfig config; protected final TokenCreator tokenCreator; private final String redirectUri; - private final String tokenEndpoint; - private final String authEndpoint; + 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(); @@ -74,6 +76,7 @@ public abstract class AuthService { 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); @@ -86,6 +89,7 @@ public abstract class AuthService { 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); @@ -96,15 +100,36 @@ public abstract class AuthService { if(!response.isSuccess()){ throw new UnableToConfigureOAuthService(this.config.getOpenIdConfigUrl(), response.code); } - this.tokenEndpoint = response.body.getToken_endpoint(); - this.authEndpoint = response.body.getAuthorization_endpoint(); + 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.tokenEndpoint = null; - this.authEndpoint = null; + 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); } @@ -128,12 +153,27 @@ public abstract class AuthService { public void sendLoginRedirectResponse(HttpServletResponse resp, String callbackUrl) { resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); - String url = this.authEndpoint!=null?String.format( + String url = this.authEndpointAbsolute !=null?String.format( "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", - this.authEndpoint, urlEncode(this.config.getClientId()), this.config.getScope(), + 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); @@ -148,41 +188,45 @@ public abstract class AuthService { } if (response != null) { if (this.doSeperateRolesRequest()) { - //long expiresAt = this.tokenCreator.getDefaultExp(Math.round(response.getExpires_in())); + 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) { - this.handleUserInfoToken(data, resp, host); + BearerToken createdToken = this.handleUserInfoToken(data, resp, host); + this.logoutTokenMap.put(createdToken.getToken(),response.getId_token()); } else { sendErrorResponse(resp, "unable to verify user"); } } else { - this.handleUserInfoToken(response.getAccess_token(), resp, host); + 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 void handleUserInfoToken(UserTokenPayload data, HttpServletResponse resp, String localHostUrl) + private BearerToken handleUserInfoToken(UserTokenPayload data, HttpServletResponse resp, String localHostUrl) throws IOException { BearerToken onapToken = this.tokenCreator.createNewJWT(data); sendTokenResponse(resp, onapToken, localHostUrl); + return onapToken; } - private void handleUserInfoToken(String accessToken, HttpServletResponse resp, String localHostUrl) + 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); - this.handleUserInfoToken(data, resp, localHostUrl); + 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; } @@ -197,12 +241,14 @@ public abstract class AuthService { 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)); } } @@ -216,6 +262,7 @@ public abstract class AuthService { 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()); @@ -226,7 +273,7 @@ public abstract class AuthService { body.append(String.format("%s=%s&", p.getKey(), urlEncode(p.getValue()))); } - String url = this.tokenEndpoint!=null?this.tokenEndpoint:this.getTokenVerifierUri(); + 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); @@ -259,6 +306,8 @@ public abstract class AuthService { return URLEncoder.encode(s, StandardCharsets.UTF_8); } + + public enum ResponseType { CODE, TOKEN, SESSION_STATE } 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 index 10f701ec2..d271948c2 100644 --- 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 @@ -62,6 +62,11 @@ public class GitlabProviderService extends AuthService { 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) { 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 05000199e..bdbf9286a 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 @@ -57,6 +57,12 @@ public class KeycloakProviderService extends AuthService { } @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 = @@ -89,6 +95,7 @@ public class KeycloakProviderService extends AuthService { 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; 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 336de5600..73bae5d4c 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 @@ -72,6 +72,11 @@ public class NextcloudProviderService extends AuthService { } @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; 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 1fb87a875..47d5fee01 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 @@ -31,6 +31,8 @@ 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; @@ -51,6 +53,8 @@ public class TokenCreator { 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( @@ -114,6 +118,7 @@ public class TokenCreator { 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); @@ -145,20 +150,46 @@ public class TokenCreator { return new Date().getTime(); } - public UserTokenPayload decode(HttpServletRequest req) throws JWTDecodeException { + 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")) { + if ((authHeader == null || !authHeader.startsWith("Bearer")) && checkCookie) { + Optional<Cookie> ocookie = + Arrays.stream(req.getCookies()).filter(c -> 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(authHeader.substring(7)); + 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; + } } diff --git a/sdnr/wt/oauth-provider/provider-jar/src/test/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/test/TestKeycloakAuthService.java b/sdnr/wt/oauth-provider/provider-jar/src/test/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/test/TestKeycloakAuthService.java index e5ec2fb32..acc7c6b36 100644 --- a/sdnr/wt/oauth-provider/provider-jar/src/test/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/test/TestKeycloakAuthService.java +++ b/sdnr/wt/oauth-provider/provider-jar/src/test/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/test/TestKeycloakAuthService.java @@ -40,6 +40,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.junit.AfterClass; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.Config; import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OAuthProviderConfig; @@ -99,7 +100,18 @@ public class TestKeycloakAuthService { public void test2() { oauthService.sendLoginRedirectResponse(null, null); } - + @Ignore + @Test + public void test3() { + HttpServletResponse resp = mock(HttpServletResponse.class); + String token = ""; + try { + oauthService.sendLogoutRedirectResponse(token, resp,"http://sdnr.onap/odlux/index.html"); + verify(resp).setStatus(302); + } catch (IOException e) { + throw new RuntimeException(e); + } + } public static class KeycloakProviderServiceToTest extends KeycloakProviderService { public KeycloakProviderServiceToTest(OAuthProviderConfig config, String redirectUri, @@ -143,6 +155,7 @@ public class TestKeycloakAuthService { public static class MyHandler implements HttpHandler { private static final String KEYCLOAK_TOKEN_ENDPOINT = "/auth/realms/onap/protocol/openid-connect/token"; + private static final String KEYCLOAK_LOGOUT_ENDPOINT = "/auth/realms/onap/protocol/openid-connect/logout"; private static final String KEYCLOAK_TOKEN_RESPONSE = loadResourceFileContent("src/test/resources/oauth/keycloak-token-response.json"); @@ -153,7 +166,12 @@ public class TestKeycloakAuthService { System.out.println(String.format("req received: %s %s", method, t.getRequestURI())); OutputStream os = null; try { - if (method.equals("POST")) { + if("GET".equals(method)){ + if(KEYCLOAK_LOGOUT_ENDPOINT.equals(uri)){ + t.sendResponseHeaders(200, 0); + } + } + else if ("POST".equals(method)) { if (uri.equals(KEYCLOAK_TOKEN_ENDPOINT)) { t.sendResponseHeaders(200, KEYCLOAK_TOKEN_RESPONSE.length()); os = t.getResponseBody(); diff --git a/sdnr/wt/oauth-provider/provider-jar/src/test/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/test/TestRealm.java b/sdnr/wt/oauth-provider/provider-jar/src/test/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/test/TestRealm.java index d6a9ac5b0..07efbcec3 100644 --- a/sdnr/wt/oauth-provider/provider-jar/src/test/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/test/TestRealm.java +++ b/sdnr/wt/oauth-provider/provider-jar/src/test/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/test/TestRealm.java @@ -43,6 +43,7 @@ import org.junit.Test; import org.onap.ccsdk.features.sdnr.wt.oauthprovider.OAuth2Realm; 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.providers.AuthService; import org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers.TokenCreator; import org.opendaylight.aaa.api.shiro.principal.ODLPrincipal; import org.opendaylight.aaa.shiro.web.env.ThreadLocals; @@ -104,6 +105,23 @@ public class TestRealm { } @Test + public void testUrlTrimming(){ + final String internalUrl="https://test.identity.onap:49333"; + final String externalUrl="https://test.identity.onap:49333"; + final String testUrl1 = "/my/token/endpoint"; + final String testUrl2 = internalUrl+testUrl1; + final String testUrl3 = externalUrl+testUrl1; + + assertEquals(testUrl1, AuthService.trimUrl(internalUrl, testUrl1)); + assertEquals(testUrl1, AuthService.trimUrl(internalUrl, testUrl2)); + assertEquals(testUrl1, AuthService.trimUrl(externalUrl, testUrl3)); + + assertEquals(testUrl2, AuthService.extendUrl(internalUrl, testUrl3)); + + + + } + @Test public void testAssertCredentialsMatch() { //bearer token use case UserTokenPayload userData = createUserData("", Arrays.asList("admin", "provision")); |