aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Dürre <michael.duerre@highstreet-technologies.com>2022-07-29 10:09:11 +0200
committerMichael Dürre <michael.duerre@highstreet-technologies.com>2022-07-29 10:09:22 +0200
commit47af0edbd755498f359ce252bf121df211f74523 (patch)
tree24ff4ef345d22c0718f8563007c511dfa01319cc
parent25423c50e504676f15c7a57c03aad40bfc35c7e6 (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>
-rw-r--r--sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthResponseData.java3
-rw-r--r--sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OpenIdConfigResponseData.java11
-rw-r--r--sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/UserTokenPayload.java13
-rw-r--r--sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/filters/CustomizedMDSALDynamicAuthorizationFilter.java34
-rw-r--r--sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/AuthHttpServlet.java22
-rw-r--r--sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/AuthService.java79
-rw-r--r--sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/GitlabProviderService.java5
-rw-r--r--sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/KeycloakProviderService.java7
-rw-r--r--sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/NextcloudProviderService.java5
-rw-r--r--sdnr/wt/oauth-provider/provider-jar/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/TokenCreator.java39
-rw-r--r--sdnr/wt/oauth-provider/provider-jar/src/test/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/test/TestKeycloakAuthService.java22
-rw-r--r--sdnr/wt/oauth-provider/provider-jar/src/test/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/test/TestRealm.java18
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"));