update oauth-provider 61/130061/1
authorMichael Dürre <michael.duerre@highstreet-technologies.com>
Fri, 29 Jul 2022 08:09:11 +0000 (10:09 +0200)
committerMichael Dürre <michael.duerre@highstreet-technologies.com>
Fri, 29 Jul 2022 08:09:22 +0000 (10:09 +0200)
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>
12 files changed:
sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthResponseData.java
sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OpenIdConfigResponseData.java
sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/UserTokenPayload.java
sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/filters/CustomizedMDSALDynamicAuthorizationFilter.java
sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/AuthHttpServlet.java
sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/AuthService.java
sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/GitlabProviderService.java
sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/KeycloakProviderService.java
sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/NextcloudProviderService.java
sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/TokenCreator.java
sdnr/wt/oauth-provider/provider-jar/src/test/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/test/TestKeycloakAuthService.java
sdnr/wt/oauth-provider/provider-jar/src/test/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/test/TestRealm.java

index 806a620..0e25b5b 100644 (file)
@@ -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
index 2af46b6..d94631f 100644 (file)
@@ -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;
+    }
+
 }
index a983dd6..f7731f0 100644 (file)
@@ -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);
+    }
 }
index 80d9d1b..b252317 100644 (file)
@@ -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);
+        }
     }
 }
index 7c88e50..7953f31 100644 (file)
@@ -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
index 192da63..2dc0b57 100644 (file)
@@ -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
     }
index 10f701e..d271948 100644 (file)
@@ -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) {
index 0500019..bdbf928 100644 (file)
@@ -56,6 +56,12 @@ public class KeycloakProviderService extends AuthService {
                 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();
@@ -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;
index 336de56..73bae5d 100644 (file)
@@ -71,6 +71,11 @@ public class NextcloudProviderService extends AuthService {
         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
index 1fb87a8..47d5fee 100644 (file)
@@ -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;
+    }
 }
index e5ec2fb..acc7c6b 100644 (file)
@@ -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();
index d6a9ac5..07efbce 100644 (file)
@@ -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;
@@ -103,6 +104,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