aboutsummaryrefslogtreecommitdiffstats
path: root/sdnr/wt/oauth-provider/oauth-core/src/main/java/org
diff options
context:
space:
mode:
Diffstat (limited to 'sdnr/wt/oauth-provider/oauth-core/src/main/java/org')
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/OAuth2Realm.java143
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/Config.java347
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/CustomObjectMapper.java39
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/InvalidConfigurationException.java32
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/KeycloakRole.java80
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/KeycloakUserTokenPayload.java231
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/NoDefinitionFoundException.java33
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthProviderConfig.java202
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthResponseData.java88
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthToken.java57
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OdlPolicy.java130
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OdlShiroConfiguration.java67
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OdlXmlMapper.java44
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OpenIdConfigResponseData.java65
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/UnableToConfigureOAuthService.java12
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/UserTokenPayload.java103
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/filters/AnyRoleHttpAuthenticationFilter.java75
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/filters/BearerAndBasicHttpAuthenticationFilter.java161
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/filters/CustomizedMDSALDynamicAuthorizationFilter.java188
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/AuthHttpServlet.java527
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/HeadersOnlyHttpServletRequest.java469
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/client/MappedBaseHttpResponse.java63
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/client/MappingBaseHttpClient.java63
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/AuthService.java356
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/GitlabProviderService.java180
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/KeycloakProviderService.java115
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/MdSalAuthorizationStore.java118
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/NextcloudProviderService.java91
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/OAuthProviderFactory.java47
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/PemUtils.java106
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/RSAKeyReader.java47
-rw-r--r--sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/TokenCreator.java202
32 files changed, 4481 insertions, 0 deletions
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/OAuth2Realm.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/OAuth2Realm.java
new file mode 100644
index 000000000..b9f3d6119
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/OAuth2Realm.java
@@ -0,0 +1,143 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider;
+
+import com.auth0.jwt.interfaces.DecodedJWT;
+import java.io.IOException;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.Config;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.InvalidConfigurationException;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers.TokenCreator;
+import org.opendaylight.aaa.api.shiro.principal.ODLPrincipal;
+import org.apache.shiro.authc.BearerToken;
+import org.opendaylight.aaa.shiro.realm.TokenAuthRealm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OAuth2Realm extends TokenAuthRealm {
+
+ public static final String REALM_NAME = "OAuth2Realm";
+ private static final Logger LOG = LoggerFactory.getLogger(OAuth2Realm.class);
+ private final TokenCreator tokenCreator;
+ private final Config config;
+
+ public OAuth2Realm() throws IllegalArgumentException, IOException, InvalidConfigurationException {
+ super();
+ super.setName(REALM_NAME);
+ this.config = Config.getInstance();
+ this.tokenCreator = TokenCreator.getInstance(this.config);
+ LOG.info("instantiated");
+ }
+
+ @Override
+ public boolean supports(AuthenticationToken token) {
+ boolean supports = (token instanceof BearerToken)
+ || (this.config.doSupportOdlUsers() && (token instanceof UsernamePasswordToken));
+ LOG.debug("supports {} is {}", token == null ? null : token.getClass().getName(), supports);
+ return supports;
+ }
+
+ @Override
+ public String getName() {
+ return REALM_NAME;
+ }
+
+ @Override
+ protected void assertCredentialsMatch(AuthenticationToken atoken, AuthenticationInfo ai)
+ throws AuthenticationException {
+ LOG.debug("assertCredentialsMatch");
+ if (atoken instanceof BearerToken) {
+ if (this.tokenCreator.verify(((BearerToken) atoken).getToken()) == null) {
+ throw new AuthenticationException("Credentials do not match");
+ }
+ } else if (this.config.doSupportOdlUsers() && (atoken instanceof UsernamePasswordToken)) {
+ //nothing to do
+ } else {
+ throw new AuthenticationException("AuthenticationInfo is not an OAuth2AuthenticationInfo");
+ }
+ }
+
+
+ // check what I can do
+ @Override
+ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg) {
+
+ LOG.debug("auth info in shiro");
+ Object principal = arg.getPrimaryPrincipal();
+ if (principal instanceof DecodedJWT) {
+ LOG.debug("detected jwt token");
+ try {
+ DecodedJWT token = (DecodedJWT) arg.getPrimaryPrincipal();
+ String[] roles = token.getClaim("roles").asArray(String.class);
+ SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
+ for (String role : roles) {
+ LOG.trace("found role {} in token", role);
+ authorizationInfo.addRole(role);
+ }
+ return authorizationInfo;
+ } catch (ClassCastException e) {
+ LOG.error("Couldn't decode authorization request", e);
+ }
+ } else if (principal instanceof ODLPrincipal) {
+ LOG.debug("detected basic token");
+ ODLPrincipal odlPrincipal = (ODLPrincipal) principal;
+ return new SimpleAuthorizationInfo(odlPrincipal.getRoles());
+ }
+ return new SimpleAuthorizationInfo();
+ }
+
+
+
+ // check who I am
+ @Override
+ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
+
+ LOG.debug("auth token in shiro:");
+ if (token instanceof UsernamePasswordToken && this.config.doSupportOdlUsers()) {
+ LOG.debug("basic auth token found");
+ return super.doGetAuthenticationInfo(token);
+ } else if (token instanceof BearerToken) {
+ LOG.debug("jwt token found");
+ BearerToken oauthToken = (BearerToken) token;
+
+ DecodedJWT jwt = this.tokenCreator.verify(oauthToken.getToken());
+ if (jwt != null) {
+ SimpleAuthenticationInfo authenticationInfo =
+ new SimpleAuthenticationInfo(jwt, token.getCredentials(), getName());
+ return authenticationInfo;
+
+ }
+ } else {
+ LOG.debug("no valid token found");
+ }
+ throw new AuthenticationException("unable to verify token " + token);
+
+ }
+
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/Config.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/Config.java
new file mode 100644
index 000000000..6798026f3
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/Config.java
@@ -0,0 +1,347 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.data;
+
+import com.fasterxml.jackson.annotation.JsonGetter;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Config {
+
+ private static final Logger LOG = LoggerFactory.getLogger(Config.class);
+ private static final String DEFAULT_CONFIGFILENAME = "etc/oauth-provider.config.json";
+ private static final String ENVVARIABLE = "${";
+ private static final String REGEXENVVARIABLE = "(\\$\\{[A-Z0-9_-]+\\})";
+ private static final Pattern pattern = Pattern.compile(REGEXENVVARIABLE);
+ private static final String DEFAULT_TOKENISSUER = "Opendaylight";
+ private static final String DEFAULT_TOKENSECRET = generateSecret();
+ private static final String DEFAULT_REDIRECTURI = "/odlux/index.html#/oauth?token=";
+ private static final String DEFAULT_SUPPORTODLUSERS = "true";
+ public static final String TOKENALG_HS256 = "HS256";
+ public static final String TOKENALG_RS256 = "RS256";
+ public static final String TOKENALG_RS512 = "RS512";
+ private static final String CLIENTALG_PRE = "Client";
+ public static final String TOKENALG_CLIENT_RS256 = CLIENTALG_PRE + TOKENALG_RS256;
+ public static final String TOKENALG_CLIENT_RS512 = CLIENTALG_PRE + TOKENALG_RS512;
+ private static final String DEFAULT_TOKEN_ALGORITHM = TOKENALG_HS256;
+
+ private static final long DEFAULT_TOKEN_LIFETIME = 30 * 60;
+ private static final List<String> VALID_ALGORITHMS =
+ Arrays.asList(TOKENALG_HS256, TOKENALG_RS256, TOKENALG_RS512, TOKENALG_CLIENT_RS256, TOKENALG_CLIENT_RS512);
+ private static final List<String> VALID_ALGORITHMS_FOR_INTERNAL_LOGIN =
+ Arrays.asList(TOKENALG_HS256, TOKENALG_RS256, TOKENALG_RS512);
+ private static SecureRandom random;
+ private static Config _instance;
+
+ private List<OAuthProviderConfig> providers;
+ private String redirectUri;
+ private String supportOdlUsers;
+ private String tokenSecret;
+ private String tokenPubKey;
+ private String algorithm;
+ private String tokenIssuer;
+ private String publicUrl;
+ private long tokenLifetime;
+
+ @Override
+ public String toString() {
+ return "Config [providers=" + providers + ", redirectUri=" + redirectUri + ", supportOdlUsers="
+ + supportOdlUsers + ", tokenSecret=***, tokenPubKey=" + tokenPubKey + ", algorithm=" + algorithm
+ + ", tokenIssuer=" + tokenIssuer + ", publicUrl=" + publicUrl + ", tokenLifetime=" + tokenLifetime
+ + "]";
+ }
+
+ public List<OAuthProviderConfig> getProviders() {
+ return providers;
+ }
+
+ public void setProviders(List<OAuthProviderConfig> providers) {
+ this.providers = providers;
+ }
+
+ public String getRedirectUri() {
+ return redirectUri;
+ }
+
+ public void setRedirectUri(String redirectUri) {
+ this.redirectUri = redirectUri;
+ }
+
+ public String getSupportOdlUsers() {
+ return supportOdlUsers;
+ }
+
+ public void setSupportOdlUsers(String supportOdlUsers) {
+ this.supportOdlUsers = supportOdlUsers;
+ }
+
+ public String getTokenSecret() {
+ return tokenSecret;
+ }
+
+ public void setTokenSecret(String tokenSecret) {
+ this.tokenSecret = tokenSecret;
+ }
+
+ public String getAlgorithm() {
+ return this.algorithm;
+ }
+
+ public void setAlgorithm(String alg) {
+ this.algorithm = alg;
+ }
+
+ @JsonGetter("tokenPubKey")
+ public String getPublicKey() {
+ return this.tokenPubKey;
+ }
+
+ @JsonSetter("tokenPubKey")
+ public void setPublicKey(String pubKey) {
+ this.tokenPubKey = pubKey;
+ }
+
+ public String getTokenIssuer() {
+ return tokenIssuer;
+ }
+
+ public void setTokenIssuer(String tokenIssuer) {
+ this.tokenIssuer = tokenIssuer;
+ }
+
+ public String getPublicUrl() {
+ return publicUrl;
+ }
+
+ public void setPublicUrl(String publicUrl) {
+ this.publicUrl = publicUrl;
+ }
+
+ public long getTokenLifetime() {
+ return this.tokenLifetime;
+ }
+
+ public void setTokenLifetime(long lifetime) {
+ this.tokenLifetime = lifetime;
+ }
+
+ @JsonIgnore
+ private void handleEnvironmentVars() {
+ if (isEnvExpression(this.tokenIssuer)) {
+ this.tokenIssuer = getProperty(this.tokenIssuer, null);
+ }
+ if (isEnvExpression(this.tokenSecret)) {
+ this.tokenSecret = getProperty(this.tokenSecret, null);
+ }
+ if (isEnvExpression(this.tokenPubKey)) {
+ this.tokenPubKey = getProperty(this.tokenPubKey, null);
+ }
+ if (isEnvExpression(this.algorithm)) {
+ this.algorithm = getProperty(this.algorithm, null);
+ }
+ if (isEnvExpression(this.publicUrl)) {
+ this.publicUrl = getProperty(this.publicUrl, null);
+ }
+ if (isEnvExpression(this.redirectUri)) {
+ this.redirectUri = getProperty(this.redirectUri, null);
+ }
+ if (isEnvExpression(this.supportOdlUsers)) {
+ this.supportOdlUsers = getProperty(this.supportOdlUsers, null);
+ }
+ if (this.providers != null && !this.providers.isEmpty()) {
+ for (OAuthProviderConfig cfg : this.providers) {
+ cfg.handleEnvironmentVars();
+ }
+ }
+ }
+
+ @JsonIgnore
+ private void handleDefaultValues() {
+ if (this.tokenIssuer == null || this.tokenIssuer.isEmpty()) {
+ this.tokenIssuer = DEFAULT_TOKENISSUER;
+ }
+ if (this.algorithm == null || this.algorithm.isEmpty()) {
+ this.algorithm = DEFAULT_TOKEN_ALGORITHM;
+ }
+ if (TOKENALG_HS256.equals(this.algorithm) && (this.tokenSecret == null || this.tokenSecret.isEmpty())) {
+ this.tokenSecret = DEFAULT_TOKENSECRET;
+ }
+ if (this.redirectUri == null || this.redirectUri.isEmpty() || "null".equals(this.redirectUri)) {
+ this.redirectUri = DEFAULT_REDIRECTURI;
+ }
+ if (this.publicUrl != null && (this.publicUrl.isEmpty() || "null".equals(this.publicUrl))) {
+ this.publicUrl = null;
+ }
+ if (this.supportOdlUsers == null || this.supportOdlUsers.isEmpty()) {
+ this.supportOdlUsers = DEFAULT_SUPPORTODLUSERS;
+ }
+ if (this.tokenLifetime <= 0) {
+ this.tokenLifetime = DEFAULT_TOKEN_LIFETIME;
+ }
+ }
+
+ static boolean isEnvExpression(String key) {
+ return key != null && key.contains(ENVVARIABLE);
+ }
+
+ public static String generateSecret() {
+ return generateSecret(30);
+ }
+
+ public static String generateSecret(int targetStringLength) {
+ int leftLimit = 48; // numeral '0'
+ int rightLimit = 122; // letter 'z'
+ if (random == null) {
+ random = new SecureRandom();
+ }
+ String generatedString = random.ints(leftLimit, rightLimit + 1)
+ .filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97)).limit(targetStringLength)
+ .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString();
+ return generatedString;
+ }
+
+ /**
+ *
+ * @param key environment var
+ * @param defValue default value if no env var found
+ * @return
+ */
+ public static String getProperty(final String key, final String defValue) {
+ String value = defValue;
+ //try to read env var
+ boolean found = false;
+ if (isEnvExpression(key)) {
+
+ LOG.info("try to find env var(s) for {}", key);
+ final Matcher matcher = pattern.matcher(key);
+ String tmp = new String(key);
+ while (matcher.find() && matcher.groupCount() > 0) {
+ final String mkey = matcher.group(1);
+ if (mkey != null) {
+ try {
+ LOG.info("match found for v={} and env key={}", key, mkey);
+ String envvar = mkey.substring(2, mkey.length() - 1);
+ String env = System.getenv(envvar);
+ tmp = tmp.replace(mkey, env == null ? "" : env);
+ if (env != null && !env.isEmpty()) {
+ found = true;
+ }
+ } catch (SecurityException e) {
+ LOG.warn("unable to read env {}: {}", key, e);
+ }
+ }
+ }
+ if (found) {
+ value = tmp;
+ }
+ }
+ return value;
+ }
+
+ public static boolean getPropertyBoolean(String key, boolean defaultValue) {
+ final String value = getProperty(key, String.valueOf(defaultValue));
+ return value.equals("true");
+ }
+
+ public static Config load(String filename) throws IOException, InvalidConfigurationException {
+ CustomObjectMapper mapper = new CustomObjectMapper();
+ File file = new File(filename);
+ if (!file.exists()) {
+ throw new FileNotFoundException();
+ }
+ String content = String.join("", Files.readAllLines(file.toPath()));
+ Config cfg = mapper.readValue(content, Config.class);
+ cfg.handleEnvironmentVars();
+ cfg.handleDefaultValues();
+ cfg.validate();
+ return cfg;
+ }
+
+
+ @JsonIgnore
+ private void validate() throws InvalidConfigurationException {
+ //verify that algorithm is supported
+ if (!VALID_ALGORITHMS.contains(this.algorithm)) {
+ throw new InvalidConfigurationException(String.format("Algorithm '%s' is not supported ", this.algorithm));
+ }
+ //verify that set values are matching the algorithm
+ //if hs256 check if secret is set
+ if (this.algorithm.startsWith("HS")) {
+ if (this.tokenSecret == null || this.tokenSecret.isBlank()) {
+ throw new InvalidConfigurationException(
+ String.format("There is no secret set for algorithm '%s'", this.algorithm));
+ }
+ }
+ //if rs256 or rs512 check if secret(private key) and pubkey are set
+ if (this.algorithm.startsWith("RS")) {
+ if (this.tokenSecret == null || this.tokenSecret.isBlank()) {
+ throw new InvalidConfigurationException(
+ String.format("There is no secret set for algorithm '%s'", this.algorithm));
+ }
+ if (this.tokenPubKey == null || this.tokenPubKey.isBlank()) {
+ throw new InvalidConfigurationException(
+ String.format("There is no public key for algorithm '%s'", this.algorithm));
+ }
+ }
+ //if client rs256 or client rs512 check if pubkey are set
+ if (this.algorithm.startsWith("Client")) {
+ if (this.tokenPubKey == null || this.tokenPubKey.isBlank()) {
+ throw new InvalidConfigurationException(
+ String.format("There is no public key for algorithm '%s'", this.algorithm));
+ }
+ }
+ }
+
+ @JsonIgnore
+ public boolean doSupportOdlUsers() {
+ return "true".equals(this.supportOdlUsers);
+ }
+
+
+ public static Config getInstance() throws IOException, InvalidConfigurationException {
+ return getInstance(DEFAULT_CONFIGFILENAME);
+ }
+
+ public static Config getInstance(String filename) throws IOException, InvalidConfigurationException {
+ if (_instance == null) {
+ _instance = load(filename);
+ }
+ return _instance;
+ }
+
+ public boolean loginActive() {
+ return VALID_ALGORITHMS_FOR_INTERNAL_LOGIN.contains(this.algorithm);
+ }
+
+
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/CustomObjectMapper.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/CustomObjectMapper.java
new file mode 100644
index 000000000..aa23d4dc1
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/CustomObjectMapper.java
@@ -0,0 +1,39 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.data;
+
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.MapperFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class CustomObjectMapper extends ObjectMapper{
+
+ private static final long serialVersionUID = 1L;
+
+
+ public CustomObjectMapper() {
+ this.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ this.setSerializationInclusion(Include.NON_NULL);
+ this.enable(MapperFeature.USE_GETTERS_AS_SETTERS);
+ }
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/InvalidConfigurationException.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/InvalidConfigurationException.java
new file mode 100644
index 000000000..a0e97de74
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/InvalidConfigurationException.java
@@ -0,0 +1,32 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.data;
+
+public class InvalidConfigurationException extends Exception {
+
+ public InvalidConfigurationException(String str) {
+ super(str);
+ }
+
+ private static final long serialVersionUID = 1L;
+
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/KeycloakRole.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/KeycloakRole.java
new file mode 100644
index 000000000..67186baa7
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/KeycloakRole.java
@@ -0,0 +1,80 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.data;
+
+public class KeycloakRole {
+ private String id;
+ private String name;
+ private String description;
+ private boolean composite;
+ private boolean clientRole;
+ private String containerId; // realmname
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isClientRole() {
+ return clientRole;
+ }
+
+ public void setClientRole(boolean clientRole) {
+ this.clientRole = clientRole;
+ }
+
+ public String getContainerId() {
+ return containerId;
+ }
+
+ public void setContainerId(String containerId) {
+ this.containerId = containerId;
+ }
+
+ public boolean isComposite() {
+ return composite;
+ }
+
+ public void setComposite(boolean composite) {
+ this.composite = composite;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+} \ No newline at end of file
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/KeycloakUserTokenPayload.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/KeycloakUserTokenPayload.java
new file mode 100644
index 000000000..c99ec0d71
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/KeycloakUserTokenPayload.java
@@ -0,0 +1,231 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.data;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.List;
+
+/**
+ * {
+ "exp": 1610362593,
+ "iat": 1610361393,
+ "jti": "09bd6f2c-5dba-44a0-bd76-cd0d440137d0",
+ "iss": "http://10.20.11.160:8080/auth/realms/onap",
+ "aud": "account",
+ "sub": "446a24bc-d8a0-43dd-afa5-e56eed75deb8",
+ "typ": "Bearer",
+ "azp": "admin-cli",
+ "session_state": "db2c96f4-cc9b-47e8-a83f-a01c50d656f2",
+ "acr": "1",
+ "realm_access": {
+ "roles": [
+ "provision",
+ "offline_access",
+ "uma_authorization"
+ ]
+ },
+ "resource_access": {
+ "account": {
+ "roles": [
+ "manage-account",
+ "manage-account-links",
+ "view-profile"
+ ]
+ }
+ },
+ "scope": "profile email",
+ "email_verified": false,
+ "name": "Luke Skywalker",
+ "preferred_username": "luke.skywalker",
+ "given_name": "Luke",
+ "family_name": "Skywalker",
+ "email": "luke.skywalker@sdnr.onap.org"
+}
+ * @author jack
+ *
+ */
+public class KeycloakUserTokenPayload {
+
+ private long exp;
+ private long iat;
+ private String jti;
+ private String iss;
+ private String aud;
+ private String sub;
+ private String typ;
+ private String azp;
+ @JsonProperty("session_state")
+ private String sessionState;
+ private String acr;
+ @JsonProperty("realm_access")
+ private RealmAccessData realmAccess;
+ @JsonProperty("resource_access")
+ private ResourceAccessData resourceAccess;
+ private String scope;
+ @JsonProperty("email_verified")
+ private String emailVerified;
+ private String name;
+ @JsonProperty("preferred_username")
+ private String preferredUsername;
+ @JsonProperty("given_name")
+ private String givenName;
+ @JsonProperty("family_name")
+ private String familyName;
+ private String email;
+
+ public long getExp() {
+ return exp;
+ }
+ public void setExp(long exp) {
+ this.exp = exp;
+ }
+ public long getIat() {
+ return iat;
+ }
+ public void setIat(long iat) {
+ this.iat = iat;
+ }
+ public String getJti() {
+ return jti;
+ }
+ public void setJti(String jti) {
+ this.jti = jti;
+ }
+ public String getIss() {
+ return iss;
+ }
+ public void setIss(String iss) {
+ this.iss = iss;
+ }
+ public String getAud() {
+ return aud;
+ }
+ public void setAud(String aud) {
+ this.aud = aud;
+ }
+ public String getSub() {
+ return sub;
+ }
+ public void setSub(String sub) {
+ this.sub = sub;
+ }
+ public String getTyp() {
+ return typ;
+ }
+ public void setTyp(String typ) {
+ this.typ = typ;
+ }
+ public String getAzp() {
+ return azp;
+ }
+ public void setAzp(String azp) {
+ this.azp = azp;
+ }
+ public String getSessionState() {
+ return sessionState;
+ }
+ public void setSessionState(String sessionState) {
+ this.sessionState = sessionState;
+ }
+ public String getAcr() {
+ return acr;
+ }
+ public void setAcr(String acr) {
+ this.acr = acr;
+ }
+ public RealmAccessData getRealmAccess() {
+ return realmAccess;
+ }
+ public void setRealmAccess(RealmAccessData realmAccess) {
+ this.realmAccess = realmAccess;
+ }
+ public ResourceAccessData getResourceAccess() {
+ return resourceAccess;
+ }
+ public void setResourceAccess(ResourceAccessData resourceAccess) {
+ this.resourceAccess = resourceAccess;
+ }
+ public String getScope() {
+ return scope;
+ }
+ public void setScope(String scope) {
+ this.scope = scope;
+ }
+ public String getEmailVerified() {
+ return emailVerified;
+ }
+ public void setEmailVerified(String emailVerified) {
+ this.emailVerified = emailVerified;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getPreferredUsername() {
+ return preferredUsername;
+ }
+ public void setPreferredUsername(String preferredUsername) {
+ this.preferredUsername = preferredUsername;
+ }
+ public String getGivenName() {
+ return givenName;
+ }
+ public void setGivenName(String givenName) {
+ this.givenName = givenName;
+ }
+ public String getFamilyName() {
+ return familyName;
+ }
+ public void setFamilyName(String familyName) {
+ this.familyName = familyName;
+ }
+ public String getEmail() {
+ return email;
+ }
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+
+ public static class RealmAccessData {
+ private List<String> roles;
+
+ public List<String> getRoles(){
+ return this.roles;
+ }
+ public void setRoles(List<String> roles) {
+ this.roles = roles;
+ }
+ }
+ public static class ResourceAccessData {
+ private RealmAccessData account;
+
+ public RealmAccessData getAccount() {
+ return this.account;
+ }
+ public void setAccount(RealmAccessData account) {
+ this.account = account;
+ }
+ }
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/NoDefinitionFoundException.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/NoDefinitionFoundException.java
new file mode 100644
index 000000000..d13be9602
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/NoDefinitionFoundException.java
@@ -0,0 +1,33 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.data;
+
+public class NoDefinitionFoundException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public NoDefinitionFoundException(String message) {
+ super(message);
+ }
+
+
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthProviderConfig.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthProviderConfig.java
new file mode 100644
index 000000000..4fb0d0069
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthProviderConfig.java
@@ -0,0 +1,202 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.data;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import java.util.HashMap;
+import java.util.Map;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers.OAuthProviderFactory.OAuthProvider;
+
+public class OAuthProviderConfig {
+
+ private String url;
+ private String internalUrl;
+ private String clientId;
+ private String secret;
+ private String id;
+ private String title;
+ private String scope;
+ private String realmName;
+ private String openIdConfigUrl;
+
+ private boolean trustAll;
+ private OAuthProvider type;
+ private Map<String, String> roleMapping;
+
+ public OAuthProvider getType() {
+ return type;
+ }
+
+ public OAuthProviderConfig(String id, String url, String internalUrl, String clientId, String secret, String scope,
+ String title, String realmName, String openIdConfigUrl, boolean trustAll) {
+ this.id = id;
+ this.url = url;
+ this.internalUrl = internalUrl;
+ this.clientId = clientId;
+ this.secret = secret;
+ this.scope = scope;
+ this.title = title;
+ this.realmName = realmName;
+ this.trustAll = trustAll;
+ this.openIdConfigUrl = openIdConfigUrl;
+ this.roleMapping = new HashMap<>();
+ }
+
+ @Override
+ public String toString() {
+ return "OAuthProviderConfig [url=" + url + ", clientId=" + clientId + ", secret=" + secret + ", id=" + id
+ + ", title=" + title + ", scope=" + scope + ", realmName=" + realmName + ", trustAll=" + trustAll
+ + ", type=" + type + ", roleMapping=" + roleMapping + "]";
+ }
+
+ public void setType(OAuthProvider type) {
+ this.type = type;
+ }
+
+ public OAuthProviderConfig() {
+ this(null, null, null, null, null, null, null, null, null, false);
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
+ }
+
+ public void setSecret(String secret) {
+ this.secret = secret;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public void setScope(String scope) {
+ this.scope = scope;
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ public String getUrl() {
+ return this.url;
+ }
+
+ public String getClientId() {
+ return this.clientId;
+ }
+
+ public String getSecret() {
+ return this.secret;
+ }
+
+ public String getTitle() {
+ return this.title;
+ }
+
+ public String getScope() {
+ return this.scope;
+ }
+
+ public String getRealmName() {
+ return realmName;
+ }
+
+ public void setRealmName(String realmName) {
+ this.realmName = realmName;
+ }
+
+ public boolean trustAll() {
+ return trustAll;
+ }
+
+ public void setTrustAll(boolean trustAll) {
+ this.trustAll = trustAll;
+ }
+
+ public Map<String, String> getRoleMapping() {
+ return roleMapping;
+ }
+
+ public void setRoleMapping(Map<String, String> roleMapping) {
+ this.roleMapping = roleMapping;
+ }
+
+ public String getInternalUrl() {
+ return internalUrl;
+ }
+
+ public void setInternalUrl(String internalUrl) {
+ this.internalUrl = internalUrl;
+ }
+
+ public void setOpenIdConfigUrl(String openIdConfigUrl){ this.openIdConfigUrl = openIdConfigUrl;}
+
+ public String getOpenIdConfigUrl() { return this.openIdConfigUrl;}
+ @JsonIgnore
+ public void handleEnvironmentVars() {
+ if (Config.isEnvExpression(this.id)) {
+ this.id = Config.getProperty(this.id, null);
+ }
+ if (Config.isEnvExpression(this.url)) {
+ this.url = Config.getProperty(this.url, null);
+ }
+ if (Config.isEnvExpression(this.internalUrl)) {
+ this.internalUrl = Config.getProperty(this.internalUrl, null);
+ }
+ if (Config.isEnvExpression(this.clientId)) {
+ this.clientId = Config.getProperty(this.clientId, null);
+ }
+ if (Config.isEnvExpression(this.secret)) {
+ this.secret = Config.getProperty(this.secret, null);
+ }
+ if (Config.isEnvExpression(this.scope)) {
+ this.scope = Config.getProperty(this.scope, null);
+ }
+ if (Config.isEnvExpression(this.title)) {
+ this.title = Config.getProperty(this.title, null);
+ }
+ if (Config.isEnvExpression(this.realmName)) {
+ this.realmName = Config.getProperty(this.realmName, null);
+ }
+ if (Config.isEnvExpression(this.openIdConfigUrl)) {
+ this.openIdConfigUrl = Config.getProperty(this.openIdConfigUrl, null);
+ }
+ }
+
+ @JsonIgnore
+ public String getUrlOrInternal() {
+ return this.internalUrl != null && this.internalUrl.length() > 0 ? this.internalUrl : this.url;
+ }
+
+ @JsonIgnore
+ public boolean hasToBeConfigured(){
+ return this.openIdConfigUrl!=null && this.openIdConfigUrl.length()>0;
+ }
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthResponseData.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthResponseData.java
new file mode 100644
index 000000000..0e25b5b0f
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthResponseData.java
@@ -0,0 +1,88 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.data;
+
+public class OAuthResponseData {
+
+ private String access_token;
+ private double expires_in;
+ private double refresh_expires_in;
+ private String refresh_token;
+ private String token_type;
+ private String id_token;
+
+ public OAuthResponseData() {
+ }
+
+ public OAuthResponseData(String token) {
+ this.access_token = token;
+ }
+
+ public String getAccess_token() {
+ return access_token;
+ }
+
+ public String getToken_type() {
+ return token_type;
+ }
+
+ public void setToken_type(String token_type) {
+ this.token_type = token_type;
+ }
+
+ public String getRefresh_token() {
+ return refresh_token;
+ }
+
+ public void setRefresh_token(String refresh_token) {
+ this.refresh_token = refresh_token;
+ }
+
+ public double getRefresh_expires_in() {
+ return refresh_expires_in;
+ }
+
+ public void setRefresh_expires_in(double refresh_expires_in) {
+ this.refresh_expires_in = refresh_expires_in;
+ }
+
+ public double getExpires_in() {
+ return expires_in;
+ }
+
+ public void setExpires_in(double expires_in) {
+ this.expires_in = expires_in;
+ }
+
+ public void setAccess_token(String access_token) {
+ 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
+ + ", refresh_expires_in=" + refresh_expires_in + ", refresh_token=" + refresh_token + ", token_type="
+ + token_type + "]";
+ }
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthToken.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthToken.java
new file mode 100644
index 000000000..0371f377d
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OAuthToken.java
@@ -0,0 +1,57 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.data;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import org.apache.shiro.authc.BearerToken;
+
+public class OAuthToken {
+ private final String access_token;
+ private final String token_type;
+ private final long expires_at;
+ private final long issued_at;
+
+ public OAuthToken(BearerToken btoken) {
+ this.access_token = btoken.getToken();
+ this.token_type = "Bearer";
+ DecodedJWT token = JWT.decode(this.access_token);
+ this.expires_at = token.getExpiresAt().getTime() / 1000L;
+ this.issued_at = token.getIssuedAt().getTime() / 1000L;
+ }
+
+ public String getAccess_token() {
+ return access_token;
+ }
+
+ public String getToken_type() {
+ return token_type;
+ }
+
+ public long getExpires_at() {
+ return expires_at;
+ }
+ public long getIssued_at() {
+ return issued_at;
+ }
+
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OdlPolicy.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OdlPolicy.java
new file mode 100644
index 000000000..19eb4b68e
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OdlPolicy.java
@@ -0,0 +1,130 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.data;
+
+public class OdlPolicy {
+
+ private String path;
+ private PolicyMethods methods;
+
+
+ public OdlPolicy() {
+
+ }
+
+ public OdlPolicy(String path, PolicyMethods methods) {
+ this.path = path;
+ this.methods = methods;
+ }
+
+ public PolicyMethods getMethods() {
+ return methods;
+ }
+
+ public void setMethods(PolicyMethods methods) {
+ this.methods = methods;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public static OdlPolicy allowAll(String path) {
+ return new OdlPolicy(path, PolicyMethods.allowAll());
+ }
+
+ public static OdlPolicy denyAll(String path) {
+ return new OdlPolicy(path, PolicyMethods.denyAll());
+ }
+
+ public static class PolicyMethods {
+ private boolean get;
+ private boolean post;
+ private boolean put;
+ private boolean delete;
+ private boolean patch;
+
+ public PolicyMethods() {
+ this(false, false, false, false, false);
+ }
+
+ public PolicyMethods(boolean get, boolean post, boolean put, boolean del, boolean patch) {
+ this.get = get;
+ this.post = post;
+ this.put = put;
+ this.delete = del;
+ this.patch = patch;
+ }
+
+ public boolean isGet() {
+ return get;
+ }
+
+ public void setGet(boolean get) {
+ this.get = get;
+ }
+
+ public boolean isPost() {
+ return post;
+ }
+
+ public void setPost(boolean post) {
+ this.post = post;
+ }
+
+ public boolean isPut() {
+ return put;
+ }
+
+ public void setPut(boolean put) {
+ this.put = put;
+ }
+
+ public boolean isDelete() {
+ return delete;
+ }
+
+ public void setDelete(boolean delete) {
+ this.delete = delete;
+ }
+
+ public boolean isPatch() {
+ return patch;
+ }
+
+ public void setPatch(boolean patch) {
+ this.patch = patch;
+ }
+
+ public static PolicyMethods allowAll() {
+ return new PolicyMethods(true, true, true, true, true);
+ }
+
+ public static PolicyMethods denyAll() {
+ return new PolicyMethods(false, false, false, false, false);
+ }
+ }
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OdlShiroConfiguration.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OdlShiroConfiguration.java
new file mode 100644
index 000000000..f5e067450
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OdlShiroConfiguration.java
@@ -0,0 +1,67 @@
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.data;
+
+import java.util.List;
+
+public class OdlShiroConfiguration {
+
+ private List<MainItem> main;
+ private List<UrlItem> urls;
+
+
+
+ public List<MainItem> getMain() {
+ return main;
+ }
+
+ public void setMain(List<MainItem> main) {
+ this.main = main;
+ }
+ public List<UrlItem> getUrls() {
+ return urls;
+ }
+ public void setUrls(List<UrlItem> urls) {
+ this.urls = urls;
+ }
+ public OdlShiroConfiguration(){
+
+ }
+
+ public static class BaseItem{
+ private String pairKey;
+ private String pairValue;
+
+ public String getPairKey() {
+ return pairKey;
+ }
+
+ public void setPairKey(String pairKey) {
+ this.pairKey = pairKey;
+ }
+
+ public String getPairValue() {
+ return pairValue;
+ }
+
+ public void setPairValue(String pairValue) {
+ this.pairValue = pairValue;
+ }
+
+ public BaseItem(){
+
+ }
+
+ }
+
+ public static class MainItem extends BaseItem{
+ public MainItem(){
+ super();
+ }
+
+ }
+ public static class UrlItem extends BaseItem{
+ public UrlItem(){
+ super();
+ }
+ }
+
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OdlXmlMapper.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OdlXmlMapper.java
new file mode 100644
index 000000000..cbdc1d0d9
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OdlXmlMapper.java
@@ -0,0 +1,44 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.data;
+
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.MapperFeature;
+import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import org.onap.ccsdk.features.sdnr.wt.yang.mapper.mapperextensions.YangToolsBuilderAnnotationIntrospector;
+
+public class OdlXmlMapper extends XmlMapper {
+
+ private static final long serialVersionUID = 1L;
+
+
+ public OdlXmlMapper() {
+ this.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ this.setSerializationInclusion(Include.NON_NULL);
+ this.setPropertyNamingStrategy(PropertyNamingStrategy.KEBAB_CASE);
+ this.enable(MapperFeature.USE_GETTERS_AS_SETTERS);
+ YangToolsBuilderAnnotationIntrospector introspector = new YangToolsBuilderAnnotationIntrospector();
+ this.setAnnotationIntrospector(introspector);
+ }
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OpenIdConfigResponseData.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OpenIdConfigResponseData.java
new file mode 100644
index 000000000..d94631fe3
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/OpenIdConfigResponseData.java
@@ -0,0 +1,65 @@
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.data;
+
+public class OpenIdConfigResponseData {
+
+ private String issuer;
+ private String authorization_endpoint;
+ private String token_endpoint;
+ private String userinfo_endpoint;
+
+ private String end_session_endpoint;
+ private String jwks_uri;
+
+ public OpenIdConfigResponseData(){
+
+ }
+
+ public String getIssuer() {
+ return issuer;
+ }
+
+ public void setIssuer(String issuer) {
+ this.issuer = issuer;
+ }
+
+ public String getAuthorization_endpoint() {
+ return authorization_endpoint;
+ }
+
+ public void setAuthorization_endpoint(String authorization_endpoint) {
+ this.authorization_endpoint = authorization_endpoint;
+ }
+
+ public String getToken_endpoint() {
+ return token_endpoint;
+ }
+
+ public void setToken_endpoint(String token_endpoint) {
+ this.token_endpoint = token_endpoint;
+ }
+
+ public String getUserinfo_endpoint() {
+ return userinfo_endpoint;
+ }
+
+ public void setUserinfo_endpoint(String userinfo_endpoint) {
+ this.userinfo_endpoint = userinfo_endpoint;
+ }
+
+ public String getJwks_uri() {
+ return jwks_uri;
+ }
+
+ 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/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/UnableToConfigureOAuthService.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/UnableToConfigureOAuthService.java
new file mode 100644
index 000000000..b791a4040
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/UnableToConfigureOAuthService.java
@@ -0,0 +1,12 @@
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.data;
+
+public class UnableToConfigureOAuthService extends Exception {
+
+ public UnableToConfigureOAuthService(String configUrl){
+ super(String.format("Unable to configure OAuth service from url %s", configUrl));
+ }
+ public UnableToConfigureOAuthService(String configUrl, int responseCode){
+ super(String.format("Unable to configure OAuth service from url %s. bad response with code %d", configUrl, responseCode));
+ }
+
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/UserTokenPayload.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/UserTokenPayload.java
new file mode 100644
index 000000000..f7731f0b8
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/data/UserTokenPayload.java
@@ -0,0 +1,103 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.data;
+
+import java.util.List;
+
+public class UserTokenPayload {
+
+ public static final String PROVIDERID_INTERNAL="Internal";
+
+ private List<String> roles;
+ private String preferredUsername;
+ private String givenName;
+ private String familyName;
+ private long exp;
+ private long iat;
+
+ private String providerId;
+
+ public long getExp() {
+ return exp;
+ }
+
+ public long getIat() {
+ return this.iat;
+ }
+
+ public void setPreferredUsername(String preferredUsername) {
+ this.preferredUsername = preferredUsername;
+ }
+
+ public void setGivenName(String givenName) {
+ this.givenName = givenName;
+ }
+
+ public void setFamilyName(String familyName) {
+ this.familyName = familyName;
+ }
+
+ public void setExp(long exp) {
+ this.exp = exp;
+ }
+
+ public void setIat(long iat) {
+ this.iat = iat;
+ }
+
+ public String getPreferredUsername() {
+ return preferredUsername;
+ }
+
+ public String getGivenName() {
+ return givenName;
+ }
+
+ public String getFamilyName() {
+ return familyName;
+ }
+
+ public List<String> getRoles() {
+ return this.roles;
+ }
+
+ public void setRoles(List<String> roles) {
+ this.roles = 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/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/filters/AnyRoleHttpAuthenticationFilter.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/filters/AnyRoleHttpAuthenticationFilter.java
new file mode 100644
index 000000000..0dc58efff
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/filters/AnyRoleHttpAuthenticationFilter.java
@@ -0,0 +1,75 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.filters;
+
+import java.util.Arrays;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import org.apache.shiro.subject.Subject;
+import org.apache.shiro.web.filter.authz.RolesAuthorizationFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Requires the requesting user to be {@link org.apache.shiro.subject.Subject#isAuthenticated() authenticated} for the
+ * request to continue, and if they're not, requires the user to login via the HTTP Bearer protocol-specific challenge.
+ * Upon successful login, they're allowed to continue on to the requested resource/url.
+ * <p/>
+ * The {@link #onAccessDenied(ServletRequest, ServletResponse)} method will only be called if the subject making the
+ * request is not {@link org.apache.shiro.subject.Subject#isAuthenticated() authenticated}
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc2617">RFC 2617</a>
+ * @see <a href="https://tools.ietf.org/html/rfc6750#section-2.1">OAuth2 Authorization Request Header Field</a>
+ * @since 1.5
+ */
+
+public class AnyRoleHttpAuthenticationFilter extends RolesAuthorizationFilter {
+
+ /**
+ * This class's private logger.
+ */
+ private static final Logger LOG = LoggerFactory.getLogger(AnyRoleHttpAuthenticationFilter.class);
+
+ @Override
+ public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
+ final Subject subject = getSubject(request, response);
+ final String[] rolesArray = (String[]) mappedValue;
+ LOG.debug("isAccessAllowed {}", Arrays.asList(rolesArray));
+
+ if (rolesArray == null || rolesArray.length == 0) {
+ //no roles specified, so nothing to check - allow access.
+ LOG.debug("no role specified: access allowed");
+ return true;
+ }
+
+ for (String roleName : rolesArray) {
+ LOG.debug("checking role {}", roleName);
+ if (subject.hasRole(roleName)) {
+ LOG.debug("role matched to {}: access allowed", roleName);
+ return true;
+ }
+ }
+ LOG.debug("no role matched: access denied");
+ return false;
+ }
+} \ No newline at end of file
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/filters/BearerAndBasicHttpAuthenticationFilter.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/filters/BearerAndBasicHttpAuthenticationFilter.java
new file mode 100644
index 000000000..51c064819
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/filters/BearerAndBasicHttpAuthenticationFilter.java
@@ -0,0 +1,161 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.filters;
+
+import java.util.Locale;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.codec.Base64;
+import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
+import org.apache.shiro.web.filter.authc.BearerHttpAuthenticationFilter;
+import org.apache.shiro.web.util.WebUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BearerAndBasicHttpAuthenticationFilter extends BearerHttpAuthenticationFilter {
+
+ // defined in lower-case for more efficient string comparison
+ private static final Logger LOG = LoggerFactory.getLogger(BearerAndBasicHttpAuthenticationFilter.class);
+ private ODLHttpAuthenticationHelperFilter basicAuthFilter;
+
+ public BearerAndBasicHttpAuthenticationFilter() {
+ this.basicAuthFilter = new ODLHttpAuthenticationHelperFilter();
+ }
+
+ protected static final String OPTIONS_HEADER = "OPTIONS";
+
+ @Override
+ protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
+ final String authHeader = this.getAuthzHeader(request);
+ if (authHeader != null && authHeader.startsWith("Basic")) {
+ return this.createBasicAuthToken(request, response);
+ }
+ return super.createToken(request, response);
+ }
+
+ @Override
+ protected String[] getPrincipalsAndCredentials(String scheme, String token) {
+ LOG.debug("getPrincipalsAndCredentials with scheme {} and token {}", scheme, token);
+ if (scheme.toLowerCase().equals("basic")) {
+ return this.basicAuthFilter.getPrincipalsAndCredentials(scheme, token);
+ }
+ return super.getPrincipalsAndCredentials(scheme, token);
+ }
+
+ @Override
+ protected boolean isLoginAttempt(String authzHeader) {
+ LOG.debug("isLoginAttempt with header {}", authzHeader);
+ if (this.basicAuthFilter.isLoginAttempt(authzHeader)) {
+ return true;
+ }
+ return super.isLoginAttempt(authzHeader);
+ }
+
+ @Override
+ protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
+ final HttpServletRequest httpRequest = WebUtils.toHttp(request);
+ final String httpMethod = httpRequest.getMethod();
+ //always allow options requests
+ if (OPTIONS_HEADER.equalsIgnoreCase(httpMethod)) {
+ return true;
+ }
+
+ if (this.basicAuthFilter.isAccessAllowed(httpRequest, response, mappedValue)) {
+ LOG.debug("isAccessAllowed succeeded on basicAuth");
+ return true;
+ }
+
+ return super.isAccessAllowed(request, response, mappedValue);
+ }
+
+ protected AuthenticationToken createBasicAuthToken(ServletRequest request, ServletResponse response) {
+ String authorizationHeader = getAuthzHeader(request);
+ if (authorizationHeader == null || authorizationHeader.length() == 0) {
+ // Create an empty authentication token since there is no
+ // Authorization header.
+ return createToken("", "", request, response);
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Attempting to execute login with headers [" + authorizationHeader + "]");
+ }
+
+ String[] prinCred = getPrincipalsAndCredentials(authorizationHeader, request);
+ if (prinCred == null || prinCred.length < 2) {
+ // Create an authentication token with an empty password,
+ // since one hasn't been provided in the request.
+ String username = prinCred == null || prinCred.length == 0 ? "" : prinCred[0];
+ return createToken(username, "", request, response);
+ }
+
+ String username = prinCred[0];
+ String password = prinCred[1];
+
+ return createToken(username, password, request, response);
+ }
+
+ private static class ODLHttpAuthenticationHelperFilter extends BasicHttpAuthenticationFilter {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ODLHttpAuthenticationHelperFilter.class);
+
+ // defined in lower-case for more efficient string comparison
+ protected static final String BEARER_SCHEME = "bearer";
+
+ protected static final String OPTIONS_HEADER = "OPTIONS";
+
+ public ODLHttpAuthenticationHelperFilter() {
+ LOG.info("Creating the ODLHttpAuthenticationFilter");
+ }
+
+ @Override
+ protected String[] getPrincipalsAndCredentials(String scheme, String encoded) {
+ final String decoded = Base64.decodeToString(encoded);
+ // attempt to decode username/password; otherwise decode as token
+ if (decoded.contains(":")) {
+ return decoded.split(":");
+ }
+ return new String[]{encoded};
+ }
+
+ @Override
+ protected boolean isLoginAttempt(String authzHeader) {
+ final String authzScheme = getAuthzScheme().toLowerCase(Locale.ROOT);
+ final String authzHeaderLowerCase = authzHeader.toLowerCase(Locale.ROOT);
+ return authzHeaderLowerCase.startsWith(authzScheme)
+ || authzHeaderLowerCase.startsWith(BEARER_SCHEME);
+ }
+
+ @Override
+ protected boolean isAccessAllowed(ServletRequest request, ServletResponse response,
+ Object mappedValue) {
+ final HttpServletRequest httpRequest = WebUtils.toHttp(request);
+ final String httpMethod = httpRequest.getMethod();
+ if (OPTIONS_HEADER.equalsIgnoreCase(httpMethod)) {
+ return true;
+ } else {
+ return super.isAccessAllowed(httpRequest, response, mappedValue);
+ }
+ }
+ }
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/filters/CustomizedMDSALDynamicAuthorizationFilter.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/filters/CustomizedMDSALDynamicAuthorizationFilter.java
new file mode 100644
index 000000000..27ca3b3f9
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/filters/CustomizedMDSALDynamicAuthorizationFilter.java
@@ -0,0 +1,188 @@
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.filters;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+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.mdsal.binding.api.ClusteredDataTreeChangeListener;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
+import org.opendaylight.mdsal.binding.api.DataTreeModification;
+import org.opendaylight.mdsal.binding.api.ReadTransaction;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.HttpAuthorization;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.permission.Permissions;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
+public class CustomizedMDSALDynamicAuthorizationFilter extends AuthorizationFilter
+ implements ClusteredDataTreeChangeListener<HttpAuthorization> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CustomizedMDSALDynamicAuthorizationFilter.class);
+
+ private static final DataTreeIdentifier<HttpAuthorization> AUTHZ_CONTAINER = DataTreeIdentifier.create(
+ LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(HttpAuthorization.class));
+
+ private static DataBroker dataBroker;
+
+ public static void setDataBroker(DataBroker dataBroker2){
+ dataBroker = dataBroker2;
+ }
+ private ListenerRegistration<?> reg;
+ private volatile ListenableFuture<Optional<HttpAuthorization>> authContainer;
+
+ public CustomizedMDSALDynamicAuthorizationFilter() {
+
+ }
+
+ @Override
+ public Filter processPathConfig(final String path, final String config) {
+ /*if (dataBroker == null){
+ throw new RuntimeException("dataBroker is not initialized");
+ }*/
+
+ return super.processPathConfig(path, config);
+ }
+
+ @Override
+ public void destroy() {
+ if (reg != null) {
+ reg.close();
+ reg = null;
+ }
+ super.destroy();
+ }
+
+ @Override
+ public void onDataTreeChanged(final Collection<DataTreeModification<HttpAuthorization>> changes) {
+ final HttpAuthorization newVal = Iterables.getLast(changes).getRootNode().getDataAfter();
+ LOG.debug("Updating authorization information to {}", newVal);
+ authContainer = Futures.immediateFuture(Optional.ofNullable(newVal));
+ }
+
+ @Override
+ public boolean isAccessAllowed(final ServletRequest request, final ServletResponse response,
+ final Object mappedValue) {
+ if (dataBroker == null){
+ throw new RuntimeException("dataBroker is not initialized");
+ }
+ if(reg == null){
+ try (ReadTransaction tx = dataBroker.newReadOnlyTransaction()) {
+ authContainer = tx.read(AUTHZ_CONTAINER.getDatastoreType(), AUTHZ_CONTAINER.getRootIdentifier());
+ }
+ reg = dataBroker.registerDataTreeChangeListener(AUTHZ_CONTAINER, this);
+ }
+ 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();
+ LOG.debug("isAccessAllowed for user={} to requestURI={}", subject, requestURI);
+
+ final Optional<HttpAuthorization> authorizationOptional;
+ try {
+ authorizationOptional = authContainer.get();
+ } catch (ExecutionException | InterruptedException e) {
+ // Something went completely wrong trying to read the authz container. Deny access.
+ LOG.warn("MDSAL attempt to read Http Authz Container failed, disallowing access", e);
+ return false;
+ }
+
+ if (!authorizationOptional.isPresent()) {
+ // The authorization container does not exist-- hence no authz rules are present
+ // Allow access.
+ LOG.debug("Authorization Container does not exist");
+ return defaultReturnValue;
+ }
+
+ final HttpAuthorization httpAuthorization = authorizationOptional.get();
+ final var policies = httpAuthorization.getPolicies();
+ 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 early since no authorization rules exist");
+ sendError(response, 403, "");
+ return defaultReturnValue;
+ }
+
+ // Sort the Policies list based on index
+ policiesList = new ArrayList<>(policiesList);
+ policiesList.sort(Comparator.comparing(Policies::getIndex));
+
+ for (Policies policy : policiesList) {
+ final String resource = policy.getResource();
+ final boolean pathsMatch = pathsMatch(resource, requestURI);
+ if (pathsMatch) {
+ 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();
+ LOG.trace("role={}", role);
+ Set<Permissions.Actions> actions = permission.getActions();
+ if (actions != null) {
+ for (Permissions.Actions action : actions) {
+ LOG.trace("action={}", action.getName());
+ if (action.getName().equalsIgnoreCase(method)) {
+ final boolean hasRole = subject.hasRole(role);
+ LOG.trace("hasRole({})={}", role, hasRole);
+ if (hasRole) {
+ return true;
+ }
+ }
+ }
+ }
+ else{
+ LOG.trace("no actions found");
+ }
+ }
+ }
+ else {
+ LOG.trace("no permissions found");
+ }
+ LOG.debug("couldn't authorize the user for access");
+ sendError(response, 403, "");
+ return false;
+ }
+ }
+ 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/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/AuthHttpServlet.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/AuthHttpServlet.java
new file mode 100644
index 000000000..562fe5472
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/AuthHttpServlet.java
@@ -0,0 +1,527 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.http;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.ShiroException;
+import org.apache.shiro.authc.BearerToken;
+import org.apache.shiro.codec.Base64;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.Subject;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.*;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OdlShiroConfiguration.MainItem;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OdlShiroConfiguration.UrlItem;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.filters.CustomizedMDSALDynamicAuthorizationFilter;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers.AuthService;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers.AuthService.PublicOAuthProviderConfig;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers.MdSalAuthorizationStore;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers.OAuthProviderFactory;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers.TokenCreator;
+import org.opendaylight.aaa.api.AuthenticationException;
+import org.opendaylight.aaa.api.Claim;
+import org.opendaylight.aaa.api.PasswordCredentialAuth;
+import org.opendaylight.aaa.api.PasswordCredentials;
+import org.opendaylight.aaa.tokenauthrealm.auth.PasswordCredentialBuilder;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.http.NamespaceException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AuthHttpServlet extends HttpServlet {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AuthHttpServlet.class.getName());
+ private static final long serialVersionUID = 1L;
+ private static final String BASEURI = "/oauth";
+ private static final String LOGINURI = BASEURI + "/login";
+ private static final String LOGOUTURI = BASEURI + "/logout";
+ private static final String PROVIDERSURI = BASEURI + "/providers";
+ public static final String REDIRECTURI = BASEURI + "/redirect";
+ private static final String REDIRECTURI_FORMAT = REDIRECTURI + "/%s";
+ private static final String POLICIESURI = BASEURI + "/policies";
+ private static final String REDIRECTID_REGEX = "^\\" + BASEURI + "\\/redirect\\/([^\\/]+)$";
+ private static final String LOGIN_REDIRECT_REGEX = "^\\" + LOGINURI + "\\/([^\\/]+)$";
+ private static final Pattern REDIRECTID_PATTERN = Pattern.compile(REDIRECTID_REGEX);
+ private static final Pattern LOGIN_REDIRECT_PATTERN = Pattern.compile(LOGIN_REDIRECT_REGEX);
+
+ private static final String DEFAULT_DOMAIN = "sdn";
+ private static final String HEAEDER_AUTHORIZATION = "Authorization";
+
+ 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 =
+ "org.opendaylight.aaa.shiro.filters.ODLHttpAuthenticationFilter2";
+ private static final String CLASSNAME_ODLMDSALAUTH =
+ "org.opendaylight.aaa.shiro.realm.MDSALDynamicAuthorizationFilter";
+ public static final String LOGIN_REDIRECT_FORMAT = LOGINURI + "/%s";
+ private static final String URI_PRE = BASEURI;
+
+ private static final String CONFIGFILE ="/opt/opendaylight/etc/opendaylight/datastore/initial/config/aaa-app-config.xml";
+ private final ObjectMapper mapper;
+ /* state <=> AuthProviderService> */
+ private final Map<String, AuthService> providerStore;
+ private final TokenCreator tokenCreator;
+ private final Config config;
+ private static MdSalAuthorizationStore mdsalAuthStore;
+ private PasswordCredentialAuth passwordCredentialAuth;
+ private OdlShiroConfiguration shiroConfiguration;
+
+ public AuthHttpServlet() throws IllegalArgumentException, IOException, InvalidConfigurationException,
+ UnableToConfigureOAuthService {
+ this(CONFIGFILE);
+ }
+ public AuthHttpServlet(String shiroconfigfile) throws IllegalArgumentException, IOException, InvalidConfigurationException,
+ UnableToConfigureOAuthService {
+ this.config = Config.getInstance();
+ this.shiroConfiguration = loadShiroConfig(shiroconfigfile);
+ this.tokenCreator = TokenCreator.getInstance(this.config);
+ this.mapper = new ObjectMapper();
+ this.providerStore = new HashMap<>();
+ for (OAuthProviderConfig pc : config.getProviders()) {
+ this.providerStore.put(pc.getId(), OAuthProviderFactory.create(pc.getType(), pc,
+ this.config.getRedirectUri(), TokenCreator.getInstance(this.config)));
+ }
+ }
+
+ public void setDataBroker(DataBroker dataBroker) {
+ CustomizedMDSALDynamicAuthorizationFilter.setDataBroker(dataBroker);
+ mdsalAuthStore = new MdSalAuthorizationStore(dataBroker);
+ }
+
+ public void setPasswordCredentialAuth(PasswordCredentialAuth passwordCredentialAuth) {
+ this.passwordCredentialAuth = passwordCredentialAuth;
+ }
+
+
+ public void onUnbindService(HttpService httpService) {
+ httpService.unregister(AuthHttpServlet.URI_PRE);
+
+ }
+
+ public void onBindService(HttpService httpService)
+ throws ServletException, NamespaceException {
+ if (httpService == null) {
+ LOG.warn("Unable to inject HttpService into loader.");
+ } else {
+ httpService.registerServlet(AuthHttpServlet.URI_PRE, this, null, null);
+ LOG.info("oauth servlet registered.");
+ }
+ }
+ private static OdlShiroConfiguration loadShiroConfig(String filename) throws IOException {
+ OdlXmlMapper mapper = new OdlXmlMapper();
+ return mapper.readValue(new File(filename), OdlShiroConfiguration.class);
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ LOG.debug("GET request for {}", req.getRequestURI());
+ getHost(req);
+ if (PROVIDERSURI.equals(req.getRequestURI())) {
+ this.sendResponse(resp, HttpServletResponse.SC_OK, getConfigs(this.providerStore.values()));
+ } else if (req.getRequestURI().startsWith(LOGINURI)) {
+ this.handleLoginRedirect(req, resp);
+ } else if (req.getRequestURI().equals(LOGOUTURI)) {
+ this.handleLogout(req, resp);
+ } else if (POLICIESURI.equals(req.getRequestURI())) {
+ this.sendResponse(resp, HttpServletResponse.SC_OK, this.getPoliciesForUser(req));
+ } else if (req.getRequestURI().startsWith(REDIRECTURI)) {
+ this.handleRedirect(req, resp);
+ } else {
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND);
+ }
+
+ }
+
+ private void handleLogout(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ final String bearerToken = this.tokenCreator.getBearerToken(req, true);
+ String redirectUrl = req.getParameter(LOGOUT_REDIRECT_URL_PARAMETER);
+ if (redirectUrl == null) {
+ redirectUrl = this.config.getPublicUrl();
+ }
+ UserTokenPayload userInfo = this.tokenCreator.decode(bearerToken);
+ if (bearerToken != null && userInfo != null && !userInfo.isInternal()) {
+ AuthService provider = this.providerStore.getOrDefault(userInfo.getProviderId(), null);
+
+ if (provider != null) {
+ provider.sendLogoutRedirectResponse(bearerToken, resp, redirectUrl);
+ this.logout();
+ return;
+ }
+ }
+ this.logout();
+ resp.sendRedirect(redirectUrl);
+
+ }
+
+ private void handleLoginRedirect(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ final String uri = req.getRequestURI();
+ final Matcher matcher = LOGIN_REDIRECT_PATTERN.matcher(uri);
+ if (matcher.find()) {
+ final String id = matcher.group(1);
+ AuthService provider = this.providerStore.getOrDefault(id, null);
+ if (provider != null) {
+ String redirectUrl = getHost(req) + String.format(REDIRECTURI_FORMAT, id);
+ provider.sendLoginRedirectResponse(resp, redirectUrl);
+ return;
+ }
+ }
+ this.sendResponse(resp, HttpServletResponse.SC_NOT_FOUND, "");
+ }
+
+ /**
+ * find out what urls can be accessed by user and which are forbidden
+ * <p>
+ * urlEntries: "anon" -> any access allowed "authcXXX" -> no grouping rule -> any access for user allowed "authcXXX,
+ * roles[abc] -> user needs to have role abc "authcXXX, roles["abc,def"] -> user needs to have roles abc AND def
+ * "authcXXX, anyroles[abc] -> user needs to have role abc "authcXXX, anyroles["abc,def"] -> user needs to have
+ * roles abc OR def
+ *
+ * @param req
+ * @return
+ */
+ private List<OdlPolicy> getPoliciesForUser(HttpServletRequest req) {
+ List<OdlPolicy> policies = new ArrayList<>();
+ List<UrlItem> urlRules = this.shiroConfiguration.getUrls();
+ UserTokenPayload data = this.getUserInfo(req);
+ if (urlRules != null) {
+ LOG.debug("try to find rules for user {} with roles {}",
+ data == null ? "null" : data.getPreferredUsername(), data == null ? "null" : data.getRoles());
+ final String regex = "^([^,]+)[,]?[\\ ]?([anyroles]+)?(\\[\"?([a-zA-Z,]+)\"?\\])?";
+ final Pattern pattern = Pattern.compile(regex);
+ Matcher matcher;
+ for (UrlItem urlRule : urlRules) {
+ matcher = pattern.matcher(urlRule.getPairValue());
+ if (matcher.find()) {
+ try {
+ final String authClass = getAuthClass(matcher.group(1));
+ Optional<OdlPolicy> policy = Optional.empty();
+ //anon access allowed
+ if (authClass == null) {
+ policy = Optional.of(OdlPolicy.allowAll(urlRule.getPairKey()));
+ } else if (authClass.equals(CLASSNAME_ODLBASICAUTH) || "authcBasic".equals(urlRule.getPairKey())) {
+ policy = isBasic(req) ? this.getTokenBasedPolicy(urlRule, matcher, data)
+ : Optional.of(OdlPolicy.denyAll(urlRule.getPairKey()));
+ } else if (authClass.equals(CLASSNAME_ODLBEARERANDBASICAUTH)) {
+ policy = this.getTokenBasedPolicy(urlRule, matcher, data);
+ } else if (authClass.equals(CLASSNAME_ODLMDSALAUTH)) {
+ policy = this.getMdSalBasedPolicy(urlRule, data);
+ }
+ if (policy.isPresent()) {
+ policies.add(policy.get());
+ } else {
+ LOG.warn("unable to get policy for authClass {} for entry {}", authClass,
+ urlRule.getPairValue());
+ policies.add(OdlPolicy.denyAll(urlRule.getPairKey()));
+ }
+ } catch (NoDefinitionFoundException e) {
+ LOG.warn("unknown authClass: ", e);
+ }
+
+ } else {
+ LOG.warn("unable to detect url role value: {}", urlRule.getPairValue());
+ }
+ }
+ } else {
+ LOG.debug("no url rules found");
+ }
+ return policies;
+ }
+
+ /**
+ * extract policy rule for user from MD-SAL not yet supported
+ *
+ * @param urlRule
+ * @param data
+ * @return
+ */
+ private Optional<OdlPolicy> getMdSalBasedPolicy(UrlItem urlRule, UserTokenPayload data) {
+ if (mdsalAuthStore != null) {
+ return data != null ? mdsalAuthStore.getPolicy(urlRule.getPairKey(), data.getRoles())
+ : Optional.of(OdlPolicy.denyAll(urlRule.getPairKey()));
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * extract policy rule for user from url rules of config
+ *
+ * @param urlRule
+ * @param matcher
+ * @param data
+ * @return
+ */
+ private Optional<OdlPolicy> getTokenBasedPolicy(UrlItem urlRule, Matcher matcher,
+ UserTokenPayload data) {
+ final String url = urlRule.getPairKey();
+ final String rule = urlRule.getPairValue();
+ if (!rule.contains(",")) {
+ LOG.debug("found rule without roles for '{}'", matcher.group(1));
+ //not important if anon or authcXXX
+ if (data != null || "anon".equals(matcher.group(1))) {
+ return Optional.of(OdlPolicy.allowAll(url));
+ }
+ }
+ if (data != null) {
+ LOG.debug("found rule with roles '{}'", matcher.group(4));
+ if ("roles".equals(matcher.group(2))) {
+ if (this.rolesMatch(data.getRoles(), Arrays.asList(matcher.group(4).split(",")), false)) {
+ return Optional.of(OdlPolicy.allowAll(url));
+ } else {
+ return Optional.of(OdlPolicy.denyAll(url));
+ }
+ } else if ("anyroles".equals(matcher.group(2))) {
+ if (this.rolesMatch(data.getRoles(), Arrays.asList(matcher.group(4).split(",")), true)) {
+ return Optional.of(OdlPolicy.allowAll(url));
+ } else {
+ return Optional.of(OdlPolicy.denyAll(url));
+ }
+ } else {
+ LOG.warn("unable to detect url role value: {}", urlRule.getPairValue());
+ }
+ } else {
+ return Optional.of(OdlPolicy.denyAll(url));
+ }
+ return Optional.empty();
+ }
+
+ private String getAuthClass(String key) throws NoDefinitionFoundException {
+ if ("anon".equals(key)) {
+ return null;
+ }
+ if("authcBasic".equals(key)){
+ return CLASSNAME_ODLBASICAUTH;
+ }
+ List<MainItem> list = shiroConfiguration.getMain();
+ Optional<MainItem> main =
+ list == null ? Optional.empty() : list.stream().filter(e -> e.getPairKey().equals(key)).findFirst();
+ if (main.isPresent()) {
+ return main.get().getPairValue();
+ }
+ throw new NoDefinitionFoundException("unable to find def for " + key);
+ }
+
+ private UserTokenPayload getUserInfo(HttpServletRequest req) {
+ if (isBearer(req)) {
+ UserTokenPayload data = this.tokenCreator.decode(req);
+ if (data != null) {
+ return data;
+ }
+ } else if (isBasic(req)) {
+ String username = getBasicAuthUsername(req);
+ if (username != null) {
+ final String domain = getBasicAuthDomain(username);
+ if (!username.contains("@")) {
+ username = String.format("%s@%s", username, domain);
+ }
+ List<String> roles = List.of();// odlIdentityService.listRoles(username, domain);
+ return UserTokenPayload.createInternal(username, roles);
+ }
+ }
+ return null;
+ }
+
+ private static String getBasicAuthDomain(String username) {
+ if (username.contains("@")) {
+ return username.split("@")[1];
+ }
+ return DEFAULT_DOMAIN;
+ }
+
+ private static String getBasicAuthUsername(HttpServletRequest req) {
+ final String header = req.getHeader(HEAEDER_AUTHORIZATION);
+ final String decoded = Base64.decodeToString(header.substring(6));
+ // attempt to decode username/password; otherwise decode as token
+ if (decoded.contains(":")) {
+ return decoded.split(":")[0];
+ }
+ LOG.warn("unable to detect username from basicauth header {}", header);
+ return null;
+ }
+
+ private static boolean isBasic(HttpServletRequest req) {
+ final String header = req.getHeader(HEAEDER_AUTHORIZATION);
+ return header != null && header.startsWith("Basic");
+ }
+
+ private static boolean isBearer(HttpServletRequest req) {
+ final String header = req.getHeader(HEAEDER_AUTHORIZATION);
+ return header != null && header.startsWith("Bearer");
+ }
+
+ private boolean rolesMatch(List<String> userRoles, List<String> policyRoles, boolean any) {
+ if (any) {
+ for (String policyRole : policyRoles) {
+ if (userRoles.contains(policyRole)) {
+ return true;
+ }
+ }
+ return false;
+ } else {
+ for (String policyRole : policyRoles) {
+ if (!userRoles.contains(policyRole)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ }
+
+ public String getHost(HttpServletRequest req) {
+ String hostUrl = this.config.getPublicUrl();
+ if (hostUrl == null) {
+ final String tmp = req.getRequestURL().toString();
+ final String regex = "^(http[s]{0,1}:\\/\\/[^\\/]+)";
+ final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
+ final Matcher matcher = pattern.matcher(tmp);
+ if (matcher.find()) {
+ hostUrl = matcher.group(1);
+ }
+ }
+ LOG.info("host={}", hostUrl);
+ return hostUrl;
+
+ }
+
+ private List<PublicOAuthProviderConfig> getConfigs(Collection<AuthService> values) {
+ List<PublicOAuthProviderConfig> configs = new ArrayList<>();
+ for (AuthService svc : values) {
+ configs.add(svc.getConfig());
+ }
+ return configs;
+ }
+
+ /**
+ * GET /oauth/redirect/{providerID}
+ *
+ * @param req
+ * @param resp
+ * @throws IOException
+ */
+ private void handleRedirect(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ final String uri = req.getRequestURI();
+ final Matcher matcher = REDIRECTID_PATTERN.matcher(uri);
+ if (matcher.find()) {
+ AuthService provider = this.providerStore.getOrDefault(matcher.group(1), null);
+ if (provider != null) {
+ //provider.setLocalHostUrl(getHost(req));
+ provider.handleRedirect(req, resp, getHost(req));
+ return;
+ }
+ }
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN);
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+
+ LOG.debug("POST request for {}", req.getRequestURI());
+ if (this.config.loginActive() && this.config.doSupportOdlUsers() && LOGINURI.equals(req.getRequestURI())) {
+ final String username = req.getParameter("username");
+ final String domain = req.getParameter("domain");
+ BearerToken token =
+ this.doLogin(username, req.getParameter("password"), domain != null ? domain : DEFAULT_DOMAIN);
+ if (token != null) {
+ sendResponse(resp, HttpServletResponse.SC_OK, new OAuthToken(token));
+ LOG.debug("login for odluser {} succeeded", username);
+ return;
+ } else {
+ LOG.debug("login failed");
+ }
+
+ }
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ }
+
+ private BearerToken doLogin(String username, String password, String domain) {
+
+ PasswordCredentials pc =
+ (new PasswordCredentialBuilder()).setUserName(username).setPassword(password).setDomain(domain).build();
+ Claim claim = null;
+ try {
+ claim = this.passwordCredentialAuth.authenticate(pc);
+ } catch (AuthenticationException e) {
+ LOG.warn("unable to authentication user {} for domain {}: ", username, domain, e);
+ }
+ if (claim != null) {
+ List<String> roles = claim.roles().stream().toList();//odlIdentityService.listRoles(username, domain);
+ UserTokenPayload data = new UserTokenPayload();
+ data.setPreferredUsername(username);
+ data.setFamilyName("");
+ data.setGivenName(username);
+ data.setIat(this.tokenCreator.getDefaultIat());
+ data.setExp(this.tokenCreator.getDefaultExp());
+ data.setRoles(roles);
+ return this.tokenCreator.createNewJWT(data);
+ } else {
+ LOG.info("unable to read auth from authservice");
+ }
+ return null;
+ }
+
+
+/* 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
+ resp.setStatus(code);
+ resp.setContentLength(output.length);
+ resp.setContentType("application/json");
+ ServletOutputStream os = resp.getOutputStream();
+ os.write(output);
+
+ }
+
+ private void logout() {
+ /* final Subject subject = SecurityUtils.getSubject();
+ try {
+ subject.logout();
+ Session session = subject.getSession(false);
+ if (session != null) {
+ session.stop();
+ }
+ } catch (ShiroException e) {
+ LOG.debug("Couldn't log out {}", subject, e);
+ }*/
+ }
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/HeadersOnlyHttpServletRequest.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/HeadersOnlyHttpServletRequest.java
new file mode 100644
index 000000000..31b6d696f
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/HeadersOnlyHttpServletRequest.java
@@ -0,0 +1,469 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.http;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.Principal;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Map;
+import javax.servlet.AsyncContext;
+import javax.servlet.DispatcherType;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpUpgradeHandler;
+import javax.servlet.http.Part;
+
+public class HeadersOnlyHttpServletRequest implements HttpServletRequest{
+
+ private final Map<String,String> headers;
+
+ public HeadersOnlyHttpServletRequest(Map<String,String> headers) {
+ this.headers = headers;
+ }
+ @Override
+ public Object getAttribute(String name) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Enumeration<String> getAttributeNames() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getCharacterEncoding() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void setCharacterEncoding(String env) throws UnsupportedEncodingException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public int getContentLength() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public long getContentLengthLong() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public String getContentType() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ServletInputStream getInputStream() throws IOException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getParameter(String name) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Enumeration<String> getParameterNames() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String[] getParameterValues(String name) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Map<String, String[]> getParameterMap() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getProtocol() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getScheme() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getServerName() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public int getServerPort() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public BufferedReader getReader() throws IOException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getRemoteAddr() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getRemoteHost() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void setAttribute(String name, Object o) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void removeAttribute(String name) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public Locale getLocale() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Enumeration<Locale> getLocales() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean isSecure() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public RequestDispatcher getRequestDispatcher(String path) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getRealPath(String path) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public int getRemotePort() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public String getLocalName() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getLocalAddr() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public int getLocalPort() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public ServletContext getServletContext() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public AsyncContext startAsync() throws IllegalStateException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse)
+ throws IllegalStateException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean isAsyncStarted() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isAsyncSupported() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public AsyncContext getAsyncContext() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public DispatcherType getDispatcherType() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getAuthType() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Cookie[] getCookies() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public long getDateHeader(String name) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public String getHeader(String name) {
+ return this.headers.getOrDefault(name,null);
+ }
+
+ @Override
+ public Enumeration<String> getHeaders(String name) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Enumeration<String> getHeaderNames() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public int getIntHeader(String name) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public String getMethod() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getPathInfo() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getPathTranslated() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getContextPath() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getQueryString() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getRemoteUser() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean isUserInRole(String role) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public Principal getUserPrincipal() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getRequestedSessionId() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getRequestURI() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public StringBuffer getRequestURL() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getServletPath() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public HttpSession getSession(boolean create) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public HttpSession getSession() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String changeSessionId() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean isRequestedSessionIdValid() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isRequestedSessionIdFromCookie() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isRequestedSessionIdFromURL() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isRequestedSessionIdFromUrl() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public void login(String username, String password) throws ServletException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void logout() throws ServletException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public Collection<Part> getParts() throws IOException, ServletException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Part getPart(String name) throws IOException, ServletException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/client/MappedBaseHttpResponse.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/client/MappedBaseHttpResponse.java
new file mode 100644
index 000000000..6b1a8eddd
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/client/MappedBaseHttpResponse.java
@@ -0,0 +1,63 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.http.client;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.onap.ccsdk.features.sdnr.wt.common.http.BaseHTTPResponse;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.CustomObjectMapper;
+
+public class MappedBaseHttpResponse<T> {
+
+
+ public static final int CODE404 = 404;
+ public static final int CODE200 = 200;
+ public static final MappedBaseHttpResponse<String> UNKNOWN = new MappedBaseHttpResponse<>(-1, null);
+ private static final ObjectMapper mapper = new CustomObjectMapper();
+ public final int code;
+ public final T body;
+
+ public MappedBaseHttpResponse(int code, String body, Class<T> clazz)
+ throws JsonMappingException, JsonProcessingException {
+ this(code, body != null ? mapper.readValue(body, clazz) : null);
+ }
+
+ private MappedBaseHttpResponse(int code, T body) {
+ this.code = code;
+ this.body = body;
+ }
+
+ public MappedBaseHttpResponse(BaseHTTPResponse response, Class<T> clazz)
+ throws JsonMappingException, JsonProcessingException {
+ this(response.code, response.body, clazz);
+ }
+
+ @Override
+ public String toString() {
+ return "BaseHTTPResponse [code=" + code + ", body=" + body + "]";
+ }
+
+ public boolean isSuccess() {
+ return this.code == CODE200;
+ }
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/client/MappingBaseHttpClient.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/client/MappingBaseHttpClient.java
new file mode 100644
index 000000000..ca455dc72
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/http/client/MappingBaseHttpClient.java
@@ -0,0 +1,63 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.http.client;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Optional;
+import org.onap.ccsdk.features.sdnr.wt.common.http.BaseHTTPClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MappingBaseHttpClient extends BaseHTTPClient {
+
+ private static Logger LOG = LoggerFactory.getLogger(MappingBaseHttpClient.class);
+
+ public MappingBaseHttpClient(String base, boolean trustAllCerts) {
+ super(base, trustAllCerts);
+ }
+
+ public MappingBaseHttpClient(String host) {
+ super(host);
+ }
+
+ public <T> Optional<MappedBaseHttpResponse<String>> sendMappedRequest(String uri, String method, String body,
+ Map<String, String> headers) {
+ return this.sendMappedRequest(uri, method, body != null ? body.getBytes(CHARSET) : null, headers, String.class);
+ }
+
+ public <T> Optional<MappedBaseHttpResponse<T>> sendMappedRequest(String uri, String method, String body,
+ Map<String, String> headers, Class<T> clazz) {
+ return this.sendMappedRequest(uri, method, body != null ? body.getBytes(CHARSET) : null, headers, clazz);
+ }
+
+ protected <T> Optional<MappedBaseHttpResponse<T>> sendMappedRequest(String uri, String method, byte[] body,
+ Map<String, String> headers, Class<T> clazz) {
+ try {
+ return Optional.of(new MappedBaseHttpResponse<T>(this.sendRequest(uri, method, body, headers), clazz));
+ } catch (IOException e) {
+ LOG.warn("problem during request for {}: ", uri, e);
+ }
+ return Optional.empty();
+ }
+
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/AuthService.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/AuthService.java
new file mode 100644
index 000000000..2dc0b5746
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/AuthService.java
@@ -0,0 +1,356 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.exceptions.JWTDecodeException;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.*;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.http.AuthHttpServlet;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.http.client.MappedBaseHttpResponse;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.http.client.MappingBaseHttpClient;
+import org.apache.shiro.authc.BearerToken;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AuthService {
+
+
+ private static final Logger LOG = LoggerFactory.getLogger(AuthService.class);
+ private final MappingBaseHttpClient httpClient;
+ protected final ObjectMapper mapper;
+ protected final OAuthProviderConfig config;
+ protected final TokenCreator tokenCreator;
+ private final String redirectUri;
+ private final String tokenEndpointRelative;
+ private final String authEndpointAbsolute;
+ private final String logoutEndpointAbsolute;
+
+ private final Map<String, String> logoutTokenMap;
+ protected abstract String getTokenVerifierUri();
+
+ protected abstract Map<String, String> getAdditionalTokenVerifierParams();
+
+ protected abstract ResponseType getResponseType();
+
+ protected abstract boolean doSeperateRolesRequest();
+
+ protected abstract UserTokenPayload mapAccessToken(String spayload)
+ throws JsonMappingException, JsonProcessingException;
+
+ protected abstract String getLoginUrl(String callbackUrl);
+ protected abstract String getLogoutUrl();
+
+ protected abstract UserTokenPayload requestUserRoles(String access_token, long issued_at, long expires_at);
+
+ protected abstract boolean verifyState(String state);
+
+ public AuthService(OAuthProviderConfig config, String redirectUri, TokenCreator tokenCreator) throws UnableToConfigureOAuthService {
+ this.config = config;
+ this.tokenCreator = tokenCreator;
+ this.redirectUri = redirectUri;
+ this.mapper = new ObjectMapper();
+ this.mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ this.httpClient = new MappingBaseHttpClient(this.config.getUrlOrInternal(), this.config.trustAll());
+ this.logoutTokenMap = new HashMap<>();
+ if (this.config.hasToBeConfigured()){
+ Optional<MappedBaseHttpResponse<OpenIdConfigResponseData>> oresponse = this.httpClient.sendMappedRequest(
+ this.config.getOpenIdConfigUrl(), "GET", null, null, OpenIdConfigResponseData.class);
+ if(oresponse.isEmpty()){
+ throw new UnableToConfigureOAuthService(this.config.getOpenIdConfigUrl());
+ }
+ MappedBaseHttpResponse<OpenIdConfigResponseData> response = oresponse.get();
+ if(!response.isSuccess()){
+ throw new UnableToConfigureOAuthService(this.config.getOpenIdConfigUrl(), response.code);
+ }
+ this.tokenEndpointRelative = trimUrl(this.config.getUrlOrInternal(),response.body.getToken_endpoint());
+ this.authEndpointAbsolute = extendUrl(this.config.getUrlOrInternal(),response.body.getAuthorization_endpoint());
+ this.logoutEndpointAbsolute = extendUrl(this.config.getUrlOrInternal(),response.body.getEnd_session_endpoint());
+ }
+ else{
+ this.tokenEndpointRelative = null;
+ this.authEndpointAbsolute = null;
+ this.logoutEndpointAbsolute = null;
+ }
+ }
+
+ public static String trimUrl(String baseUrl, String endpoint) {
+ if(endpoint.startsWith(baseUrl)){
+ return endpoint.substring(baseUrl.length());
+ }
+ if(endpoint.startsWith("http")){
+ return endpoint.substring(endpoint.indexOf("/",8));
+ }
+ return endpoint;
+ }
+ public static String extendUrl(String baseUrl, String endpoint) {
+ if(endpoint.startsWith("http")){
+ endpoint= endpoint.substring(endpoint.indexOf("/",8));
+ }
+ if(baseUrl.endsWith("/")){
+ baseUrl=baseUrl.substring(0,baseUrl.length()-2);
+ }
+ return baseUrl+endpoint;
+ }
+
+ public PublicOAuthProviderConfig getConfig() {
+ return new PublicOAuthProviderConfig(this);
+ }
+
+ protected MappingBaseHttpClient getHttpClient() {
+ return this.httpClient;
+ }
+
+ public void handleRedirect(HttpServletRequest req, HttpServletResponse resp, String host) throws IOException {
+ switch (this.getResponseType()) {
+ case CODE:
+ this.handleRedirectCode(req, resp, host);
+ break;
+ case TOKEN:
+ sendErrorResponse(resp, "not yet implemented");
+ break;
+ case SESSION_STATE:
+ break;
+ }
+ }
+
+ public void sendLoginRedirectResponse(HttpServletResponse resp, String callbackUrl) {
+ resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
+ String url = this.authEndpointAbsolute !=null?String.format(
+ "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s",
+ this.authEndpointAbsolute, urlEncode(this.config.getClientId()), this.config.getScope(),
+ urlEncode(callbackUrl)):this.getLoginUrl(callbackUrl);
+ resp.setHeader("Location", url);
+ }
+ public void sendLogoutRedirectResponse(String token, HttpServletResponse resp, String redirectUrl)
+ throws IOException {
+ String idToken = this.logoutTokenMap.getOrDefault(token, null);
+ String logoutEndpoint = this.logoutEndpointAbsolute!=null?this.logoutEndpointAbsolute:this.getLogoutUrl();
+ if(idToken==null) {
+ LOG.debug("unable to find token in map. Do unsafe logout.");
+ resp.sendRedirect(this.logoutEndpointAbsolute);
+ return;
+ }
+ LOG.debug("id token found. redirect to specific logout");
+ resp.sendRedirect(String.format("%s?id_token_hint=%s&post_logout_redirect_uri=%s",logoutEndpoint, idToken,
+ urlEncode(redirectUrl)));
+ }
+
+
+
+ private static void sendErrorResponse(HttpServletResponse resp, String message) throws IOException {
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+ }
+
+ private void handleRedirectCode(HttpServletRequest req, HttpServletResponse resp, String host) throws IOException {
+ final String code = req.getParameter("code");
+ final String state = req.getParameter("state");
+ OAuthResponseData response = null;
+ if(this.verifyState(state)) {
+ response = this.getTokenForUser(code, host);
+ }
+ if (response != null) {
+ if (this.doSeperateRolesRequest()) {
+ LOG.debug("do a seperate role request");
+ long expiresAt = this.tokenCreator.getDefaultExp();
+ long issuedAt = this.tokenCreator.getDefaultIat();
+ UserTokenPayload data = this.requestUserRoles(response.getAccess_token(), issuedAt, expiresAt);
+ if (data != null) {
+ BearerToken createdToken = this.handleUserInfoToken(data, resp, host);
+ this.logoutTokenMap.put(createdToken.getToken(),response.getId_token());
+ } else {
+ sendErrorResponse(resp, "unable to verify user");
+ }
+ } else {
+ BearerToken createdToken = this.handleUserInfoToken(response.getAccess_token(), resp, host);
+ this.logoutTokenMap.put(createdToken.getToken(),response.getId_token());
+ }
+ } else {
+ sendErrorResponse(resp, "unable to verify code");
+ }
+ }
+
+ private BearerToken handleUserInfoToken(UserTokenPayload data, HttpServletResponse resp, String localHostUrl)
+ throws IOException {
+ BearerToken onapToken = this.tokenCreator.createNewJWT(data);
+ sendTokenResponse(resp, onapToken, localHostUrl);
+ return onapToken;
+ }
+
+ private BearerToken handleUserInfoToken(String accessToken, HttpServletResponse resp, String localHostUrl)
+ throws IOException {
+ try {
+ DecodedJWT jwt = JWT.decode(accessToken);
+ String spayload = base64Decode(jwt.getPayload());
+ LOG.debug("payload in jwt='{}'", spayload);
+ UserTokenPayload data = this.mapAccessToken(spayload);
+ return this.handleUserInfoToken(data, resp, localHostUrl);
+ } catch (JWTDecodeException | JsonProcessingException e) {
+ LOG.warn("unable to decode jwt token {}: ", accessToken, e);
+ sendErrorResponse(resp, e.getMessage());
+ }
+ return null;
+ }
+
+
+ protected List<String> mapRoles(List<String> roles) {
+ final Map<String, String> map = this.config.getRoleMapping();
+ return roles.stream().map(r -> map.getOrDefault(r, r)).collect(Collectors.toList());
+ }
+
+ private void sendTokenResponse(HttpServletResponse resp, BearerToken data, String localHostUrl) throws IOException {
+ if (this.redirectUri == null) {
+ byte[] output = data != null ? mapper.writeValueAsString(data).getBytes() : new byte[0];
+ resp.setStatus(200);
+ resp.setContentLength(output.length);
+ resp.setContentType("application/json");
+ resp.addCookie(this.tokenCreator.createAuthCookie(data));
+ ServletOutputStream os = null;
+ os = resp.getOutputStream();
+ os.write(output);
+ } else {
+ resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
+ resp.setHeader("Location", assembleUrl(localHostUrl, this.redirectUri, data.getToken()));
+ resp.addCookie(this.tokenCreator.createAuthCookie(data));
+ }
+ }
+
+
+
+ private static String base64Decode(String data) {
+ return new String(Base64.getDecoder().decode(data), StandardCharsets.UTF_8);
+ }
+
+ private OAuthResponseData getTokenForUser(String code, String localHostUrl) {
+
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Content-Type", "application/x-www-form-urlencoded");
+ headers.put("Accept", "application/json");
+ Map<String, String> params = this.getAdditionalTokenVerifierParams();
+ params.put("code", code);
+ params.put("client_id", this.config.getClientId());
+ params.put("client_secret", this.config.getSecret());
+ params.put("redirect_uri", assembleRedirectUrl(localHostUrl, AuthHttpServlet.REDIRECTURI, this.config.getId()));
+ StringBuilder body = new StringBuilder();
+ for (Entry<String, String> p : params.entrySet()) {
+ body.append(String.format("%s=%s&", p.getKey(), urlEncode(p.getValue())));
+ }
+
+ String url = this.tokenEndpointRelative !=null?this.tokenEndpointRelative :this.getTokenVerifierUri();
+ Optional<MappedBaseHttpResponse<OAuthResponseData>> response =
+ this.httpClient.sendMappedRequest(url, "POST",
+ body.substring(0, body.length() - 1), headers, OAuthResponseData.class);
+ if (response.isPresent() && response.get().isSuccess()) {
+ return response.get().body;
+ }
+ LOG.warn("problem get token for code {}", code);
+
+ return null;
+ }
+
+ /**
+ * Assemble callback url for service provider {host}{baseUri}/{serviceId} e.g.
+ * http://10.20.0.11:8181/oauth/redirect/keycloak
+ *
+ * @param host
+ * @param baseUri
+ * @param serviceId
+ * @return
+ */
+ public static String assembleRedirectUrl(String host, String baseUri, String serviceId) {
+ return String.format("%s%s/%s", host, baseUri, serviceId);
+ }
+
+ private static String assembleUrl(String host, String uri, String token) {
+ return String.format("%s%s%s", host, uri, token);
+ }
+
+ public static String urlEncode(String s) {
+ return URLEncoder.encode(s, StandardCharsets.UTF_8);
+ }
+
+
+
+ public enum ResponseType {
+ CODE, TOKEN, SESSION_STATE
+ }
+
+
+ public static class PublicOAuthProviderConfig {
+
+ private String id;
+ private String title;
+ private String loginUrl;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getLoginUrl() {
+ return loginUrl;
+ }
+
+ public void setLoginUrl(String loginUrl) {
+ this.loginUrl = loginUrl;
+ }
+
+ public PublicOAuthProviderConfig(AuthService authService) {
+ this.id = authService.config.getId();
+ this.title = authService.config.getTitle();
+ this.loginUrl = String.format(AuthHttpServlet.LOGIN_REDIRECT_FORMAT, authService.config.getId());
+ }
+
+ }
+
+
+
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/GitlabProviderService.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/GitlabProviderService.java
new file mode 100644
index 000000000..d271948c2
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/GitlabProviderService.java
@@ -0,0 +1,180 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.Config;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OAuthProviderConfig;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UnableToConfigureOAuthService;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UserTokenPayload;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.http.client.MappedBaseHttpResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GitlabProviderService extends AuthService {
+
+ private static final Logger LOG = LoggerFactory.getLogger(GitlabProviderService.class);
+ private Map<String, String> additionalTokenVerifierParams;
+ protected final List<String> randomIds;
+ private static final String API_USER_URI = "/api/v4/user";
+ private static final String API_GROUP_URI = "/api/v4/groups?min_access_level=10";
+
+ public GitlabProviderService(OAuthProviderConfig config, String redirectUri, TokenCreator tokenCreator) throws UnableToConfigureOAuthService {
+ super(config, redirectUri, tokenCreator);
+ this.additionalTokenVerifierParams = new HashMap<>();
+ this.additionalTokenVerifierParams.put("grant_type", "authorization_code");
+ this.randomIds = new ArrayList<>();
+ }
+
+ @Override
+ protected String getTokenVerifierUri() {
+ return "/oauth/token";
+ }
+
+ @Override
+ protected String getLoginUrl(String callbackUrl) {
+ return String.format("%s/oauth/authorize?client_id=%s&response_type=code&state=%s&redirect_uri=%s",
+ this.config.getUrl(), urlEncode(this.config.getClientId()), this.createRandomId(), callbackUrl);
+ }
+
+ @Override
+ protected String getLogoutUrl() {
+ return String.format("%s/oauth/logout", this.config.getUrl());
+ }
+
+ private String createRandomId() {
+ String rnd = null;
+ while(true) {
+ rnd=Config.generateSecret(20);
+ if(!this.randomIds.contains(rnd)) {
+ break;
+ }
+ }
+ this.randomIds.add(rnd);
+ return rnd;
+ }
+
+ @Override
+ protected ResponseType getResponseType() {
+ return ResponseType.CODE;
+ }
+
+ @Override
+ protected Map<String, String> getAdditionalTokenVerifierParams() {
+ return this.additionalTokenVerifierParams;
+
+ }
+
+ @Override
+ protected boolean doSeperateRolesRequest() {
+ return true;
+ }
+
+ @Override
+ protected UserTokenPayload mapAccessToken(String spayload) throws JsonMappingException, JsonProcessingException {
+ return null;
+ }
+
+ @Override
+ protected UserTokenPayload requestUserRoles(String access_token, long issued_at, long expires_at) {
+ LOG.info("reqesting user roles with token={}", access_token);
+ Map<String, String> authHeaders = new HashMap<>();
+ authHeaders.put("Authorization", String.format("Bearer %s", access_token));
+ Optional<MappedBaseHttpResponse<GitlabUserInfo>> userInfo =
+ this.getHttpClient().sendMappedRequest(API_USER_URI, "GET", null, authHeaders, GitlabUserInfo.class);
+ if (userInfo.isEmpty()) {
+ LOG.warn("unable to read user data");
+ return null;
+ }
+ Optional<MappedBaseHttpResponse<GitlabGroupInfo[]>> groupInfos = this.getHttpClient()
+ .sendMappedRequest(API_GROUP_URI, "GET", null, authHeaders, GitlabGroupInfo[].class);
+ if (groupInfos.isEmpty()) {
+ LOG.warn("unable to read group information for user");
+ return null;
+ }
+ UserTokenPayload data = new UserTokenPayload();
+ GitlabUserInfo uInfo = userInfo.get().body;
+ data.setPreferredUsername(uInfo.getUsername());
+ data.setGivenName(uInfo.getName());
+ data.setFamilyName(uInfo.getName());
+ data.setIat(issued_at);
+ data.setExp(expires_at);
+ List<String> roles = new ArrayList<>();
+ GitlabGroupInfo[] uRoles = groupInfos.get().body;
+ for (GitlabGroupInfo uRole : uRoles) {
+ roles.add(uRole.getName());
+ }
+ data.setRoles(this.mapRoles(roles));
+ return data;
+ }
+
+
+
+ @SuppressWarnings("unused")
+ private static class GitlabUserInfo {
+
+ private String username;
+ private String name;
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ }
+ @SuppressWarnings("unused")
+ private static class GitlabGroupInfo {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ }
+ @Override
+ protected boolean verifyState(String state) {
+ if(this.randomIds.contains(state)) {
+ this.randomIds.remove(state);
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/KeycloakProviderService.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/KeycloakProviderService.java
new file mode 100644
index 000000000..bdbf9286a
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/KeycloakProviderService.java
@@ -0,0 +1,115 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.KeycloakUserTokenPayload;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OAuthProviderConfig;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UnableToConfigureOAuthService;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UserTokenPayload;
+
+public class KeycloakProviderService extends AuthService {
+
+ public static final String ID = "keycloak";
+ private Map<String, String> additionalTokenVerifierParams;
+
+ public KeycloakProviderService(OAuthProviderConfig config, String redirectUri, TokenCreator tokenCreator) throws UnableToConfigureOAuthService {
+ super(config, redirectUri, tokenCreator);
+ this.additionalTokenVerifierParams = new HashMap<>();
+ this.additionalTokenVerifierParams.put("grant_type", "authorization_code");
+ }
+
+ @Override
+ protected String getTokenVerifierUri() {
+ return String.format("/auth/realms/%s/protocol/openid-connect/token", urlEncode(this.config.getRealmName()));
+ }
+
+ @Override
+ protected String getLoginUrl(String callbackUrl) {
+ return String.format(
+ "%s/auth/realms/%s/protocol/openid-connect/auth?client_id=%s&response_type=code&scope=%s&redirect_uri=%s",
+ this.config.getUrl(), urlEncode(this.config.getRealmName()), urlEncode(this.config.getClientId()),
+ this.config.getScope(), urlEncode(callbackUrl));
+ }
+
+ @Override
+ protected String getLogoutUrl() {
+ return String.format("%s/auth/realms/%s/protocol/openid-connect/logout", this.config.getUrl(),
+ urlEncode(this.config.getRealmName()));
+ }
+
+ @Override
+ protected List<String> mapRoles(List<String> data) {
+ final Map<String, String> map = this.config.getRoleMapping();
+ List<String> filteredRoles =
+ data.stream().filter(role -> !role.equals("uma_authorization") && !role.equals("offline_access"))
+ .map(r -> map.getOrDefault(r, r)).collect(Collectors.toList());
+ return filteredRoles;
+ }
+
+ @Override
+ protected ResponseType getResponseType() {
+ return ResponseType.CODE;
+ }
+
+ @Override
+ protected Map<String, String> getAdditionalTokenVerifierParams() {
+ return this.additionalTokenVerifierParams;
+
+ }
+
+ @Override
+ protected boolean doSeperateRolesRequest() {
+ return false;
+ }
+
+ @Override
+ protected UserTokenPayload mapAccessToken(String spayload) throws JsonMappingException, JsonProcessingException {
+ KeycloakUserTokenPayload payload = mapper.readValue(spayload, KeycloakUserTokenPayload.class);
+ UserTokenPayload data = new UserTokenPayload();
+ data.setIat(payload.getIat() * 1000L);
+ data.setExp(payload.getExp() * 1000L);
+ data.setFamilyName(payload.getFamilyName());
+ data.setGivenName(payload.getGivenName());
+ data.setProviderId(this.config.getId());
+ data.setPreferredUsername(payload.getPreferredUsername());
+ data.setRoles(this.mapRoles(payload.getRealmAccess().getRoles()));
+ return data;
+ }
+
+ @Override
+ protected UserTokenPayload requestUserRoles(String access_token, long issued_at, long expires_at) {
+ return null;
+ }
+
+ @Override
+ protected boolean verifyState(String state) {
+ return true;
+ }
+
+
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/MdSalAuthorizationStore.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/MdSalAuthorizationStore.java
new file mode 100644
index 000000000..4bf35e72d
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/MdSalAuthorizationStore.java
@@ -0,0 +1,118 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OdlPolicy;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OdlPolicy.PolicyMethods;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.ReadTransaction;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.HttpAuthorization;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.Policies;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.permission.Permissions;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.permission.Permissions.Actions;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MdSalAuthorizationStore {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MdSalAuthorizationStore.class.getName());
+
+ private final DataBroker dataBroker;
+
+ public MdSalAuthorizationStore(DataBroker dataBroker) {
+ this.dataBroker = dataBroker;
+ }
+
+ public Optional<OdlPolicy> getPolicy(String path, List<String> userRoles) {
+ InstanceIdentifier<Policies> iif = InstanceIdentifier.create(HttpAuthorization.class).child(Policies.class);
+ Optional<Policies> odata = Optional.empty();
+ // The implicite close is not handled correctly by underlaying opendaylight netconf service
+ ReadTransaction transaction = this.dataBroker.newReadOnlyTransaction();
+ try {
+ odata = transaction.read(LogicalDatastoreType.CONFIGURATION, iif).get();
+ } catch (ExecutionException e) {
+ LOG.warn("unable to read policies from mdsal: ", e);
+ } catch (InterruptedException e) {
+ LOG.warn("Interrupted!", e);
+ // Restore interrupted state...
+ Thread.currentThread().interrupt();
+ }
+ if (odata.isEmpty()) {
+ return Optional.empty();
+ }
+ List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies> data =
+ odata.get().getPolicies();
+ if (data == null) {
+ return Optional.empty();
+ }
+ Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies> entry =
+ data.stream().filter(e -> path.equals(e.getResource())).findFirst();
+ if (entry.isEmpty()) {
+ return Optional.empty();
+ }
+ List<Permissions> permissions = entry.get().getPermissions();
+ if (permissions == null) {
+ return Optional.empty();
+ }
+ Optional<Permissions> rolePm = permissions.stream().filter((e) -> userRoles.contains(e.getRole())).findFirst();
+ if (rolePm.isEmpty()) {
+ return Optional.empty();
+ }
+ return Optional.of(mapPolicy(path, rolePm.get().getActions()));
+ }
+
+ private OdlPolicy mapPolicy(String path, Set<Actions> actions) {
+ PolicyMethods methods = new PolicyMethods();
+ String action;
+ for (Actions a : actions) {
+ action = a.getName().toLowerCase();
+ switch (action) {
+ case "get":
+ methods.setGet(true);
+ break;
+ case "post":
+ methods.setPost(true);
+ break;
+ case "put":
+ methods.setPut(true);
+ break;
+ case "delete":
+ methods.setDelete(true);
+ break;
+ case "patch":
+ methods.setPatch(true);
+ break;
+ default:
+ LOG.warn("unknown http method {}", action);
+ break;
+ }
+ }
+ return new OdlPolicy(path, methods);
+ }
+
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/NextcloudProviderService.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/NextcloudProviderService.java
new file mode 100644
index 000000000..73bae5d4c
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/NextcloudProviderService.java
@@ -0,0 +1,91 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import java.util.Map;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OAuthProviderConfig;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UnableToConfigureOAuthService;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UserTokenPayload;
+
+public class NextcloudProviderService extends AuthService {
+
+ public NextcloudProviderService(OAuthProviderConfig config, String redirectUri, TokenCreator tokenCreator) throws UnableToConfigureOAuthService {
+ super(config, redirectUri, tokenCreator);
+ // TODO Auto-generated constructor stub
+ }
+
+ @Override
+ protected String getTokenVerifierUri() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected Map<String, String> getAdditionalTokenVerifierParams() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected ResponseType getResponseType() {
+ // TODO Auto-generated method stub
+ return ResponseType.TOKEN;
+ }
+
+ @Override
+ protected boolean doSeperateRolesRequest() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ protected UserTokenPayload mapAccessToken(String spayload) throws JsonMappingException, JsonProcessingException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected String getLoginUrl(String callbackUrl) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected String getLogoutUrl() {
+ return null;
+ }
+
+ @Override
+ protected UserTokenPayload requestUserRoles(String access_token, long issued_at, long expires_at) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected boolean verifyState(String state) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+} \ No newline at end of file
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/OAuthProviderFactory.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/OAuthProviderFactory.java
new file mode 100644
index 000000000..152569930
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/OAuthProviderFactory.java
@@ -0,0 +1,47 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers;
+
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OAuthProviderConfig;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UnableToConfigureOAuthService;
+
+public class OAuthProviderFactory {
+
+
+ public static AuthService create(OAuthProvider key, OAuthProviderConfig config, String redirectUri,
+ TokenCreator tokenCreator) throws UnableToConfigureOAuthService {
+ switch (key) {
+ case KEYCLOAK:
+ return new KeycloakProviderService(config, redirectUri, tokenCreator);
+ case NEXTCLOUD:
+ return new NextcloudProviderService(config, redirectUri, tokenCreator);
+ case GITLAB:
+ return new GitlabProviderService(config, redirectUri, tokenCreator);
+ }
+ return null;
+ }
+
+ public static enum OAuthProvider {
+ KEYCLOAK, NEXTCLOUD, GITLAB
+ }
+
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/PemUtils.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/PemUtils.java
new file mode 100644
index 000000000..fac46f6b1
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/PemUtils.java
@@ -0,0 +1,106 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.EncodedKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import org.bouncycastle.util.io.pem.PemObject;
+import org.bouncycastle.util.io.pem.PemReader;
+
+public class PemUtils {
+
+ private static byte[] parsePEMFile(File pemFile) throws IOException {
+ if (!pemFile.isFile() || !pemFile.exists()) {
+ throw new FileNotFoundException(String.format("The file '%s' doesn't exist.", pemFile.getAbsolutePath()));
+ }
+ return parsePEMFile(new FileReader(pemFile));
+ }
+ private static byte[] parsePEMFile(Reader inputReader) throws IOException {
+ PemReader reader = new PemReader(inputReader);
+ PemObject pemObject = reader.readPemObject();
+ byte[] content = pemObject.getContent();
+ reader.close();
+ return content;
+ }
+ private static PublicKey getPublicKey(byte[] keyBytes, String algorithm) {
+ PublicKey publicKey = null;
+ try {
+ KeyFactory kf = KeyFactory.getInstance(algorithm);
+ EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
+ publicKey = kf.generatePublic(keySpec);
+ } catch (NoSuchAlgorithmException e) {
+ System.out.println("Could not reconstruct the public key, the given algorithm could not be found.");
+ } catch (InvalidKeySpecException e) {
+ System.out.println("Could not reconstruct the public key");
+ }
+
+ return publicKey;
+ }
+
+ private static PrivateKey getPrivateKey(byte[] keyBytes, String algorithm) {
+ PrivateKey privateKey = null;
+ try {
+ KeyFactory kf = KeyFactory.getInstance(algorithm);
+ EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
+ privateKey = kf.generatePrivate(keySpec);
+ } catch (NoSuchAlgorithmException e) {
+ System.out.println("Could not reconstruct the private key, the given algorithm could not be found.");
+ } catch (InvalidKeySpecException e) {
+ System.out.println("Could not reconstruct the private key");
+ }
+
+ return privateKey;
+ }
+
+ public static PublicKey readPublicKeyFromFile(String filepath, String algorithm) throws IOException {
+ byte[] bytes = PemUtils.parsePEMFile(new File(filepath));
+ return PemUtils.getPublicKey(bytes, algorithm);
+ }
+
+ public static PublicKey readPublicKey(String filecontent, String algorithm) throws IOException {
+ byte[] bytes = PemUtils.parsePEMFile(new StringReader(filecontent));
+ return PemUtils.getPublicKey(bytes, algorithm);
+ }
+
+ public static PrivateKey readPrivateKeyFromFile(String filepath, String algorithm) throws IOException {
+ byte[] bytes = PemUtils.parsePEMFile(new File(filepath));
+ return PemUtils.getPrivateKey(bytes, algorithm);
+ }
+
+ public static PrivateKey readPrivateKey(String filecontent, String algorithm) throws IOException {
+ byte[] bytes = PemUtils.parsePEMFile(new StringReader(filecontent));
+ return PemUtils.getPrivateKey(bytes, algorithm);
+ }
+
+} \ No newline at end of file
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/RSAKeyReader.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/RSAKeyReader.java
new file mode 100644
index 000000000..028dff9dd
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/RSAKeyReader.java
@@ -0,0 +1,47 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers;
+
+import java.io.IOException;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+
+public class RSAKeyReader {
+
+ private static final String PREFIX_FILEURL = "file://";
+
+ public static RSAPrivateKey getPrivateKey(String filenameOrContent) throws IOException {
+ if (filenameOrContent.startsWith(PREFIX_FILEURL)) {
+ return (RSAPrivateKey) PemUtils.readPrivateKeyFromFile(filenameOrContent.substring(PREFIX_FILEURL.length()),
+ "RSA");
+ }
+ return (RSAPrivateKey) PemUtils.readPrivateKey(filenameOrContent, "RSA");
+ }
+
+ public static RSAPublicKey getPublicKey(String filenameOrContent) throws IOException {
+ if (filenameOrContent.startsWith(PREFIX_FILEURL)) {
+ return (RSAPublicKey) PemUtils.readPublicKeyFromFile(filenameOrContent.substring(PREFIX_FILEURL.length()),
+ "RSA");
+ }
+ return (RSAPublicKey) PemUtils.readPublicKey(filenameOrContent, "RSA");
+ }
+}
diff --git a/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/TokenCreator.java b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/TokenCreator.java
new file mode 100644
index 000000000..d8720e823
--- /dev/null
+++ b/sdnr/wt/oauth-provider/oauth-core/src/main/java/org/onap/ccsdk/features/sdnr/wt/oauthprovider/providers/TokenCreator.java
@@ -0,0 +1,202 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP : ccsdk features
+ * ================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ */
+package org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.JWTDecodeException;
+import com.auth0.jwt.exceptions.JWTVerificationException;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.auth0.jwt.interfaces.JWTVerifier;
+import java.io.IOException;
+import java.security.Security;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Optional;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.Config;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UserTokenPayload;
+import org.onap.ccsdk.features.sdnr.wt.oauthprovider.http.AuthHttpServlet;
+import org.apache.shiro.authc.BearerToken;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TokenCreator {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AuthHttpServlet.class.getName());
+ private final String issuer;
+ private static TokenCreator _instance;
+ private final long tokenLifetimeSeconds;
+ private final Algorithm algorithm;
+
+ private static final String ROLES_CLAIM = "roles";
+ private static final String FAMILYNAME_CLAIM = "family_name";
+ private static final String NAME_CLAIM = "name";
+ private static final String PROVIDERID_CLAIM = "provider_id";
+ private static final String COOKIE_NAME_AUTH = "token";
+
+ static {
+ Security.addProvider(
+ new BouncyCastleProvider()
+ );
+ }
+ public static TokenCreator getInstance(Config config) throws IllegalArgumentException, IOException {
+ if (_instance == null) {
+ _instance = new TokenCreator(config);
+ }
+ return _instance;
+ }
+
+ public static TokenCreator getInstance(String alg, String secret, String issuer, long tokenLifetime)
+ throws IllegalArgumentException, IOException {
+ return getInstance(alg, secret, null, issuer, tokenLifetime);
+ }
+
+ public static TokenCreator getInstance(String alg, String secret, String pubkey, String issuer, long tokenLifetime)
+ throws IllegalArgumentException, IOException {
+ if (_instance == null) {
+ _instance = new TokenCreator(alg, secret, pubkey, issuer, tokenLifetime);
+ }
+ return _instance;
+ }
+
+ private TokenCreator(Config config) throws IllegalArgumentException, IOException {
+ this(config.getAlgorithm(), config.getTokenSecret(), config.getPublicKey(), config.getTokenIssuer(),
+ config.getTokenLifetime());
+ }
+
+ private TokenCreator(String alg, String secret, String pubkey, String issuer, long tokenLifetime)
+ throws IllegalArgumentException, IOException {
+ this.issuer = issuer;
+ this.tokenLifetimeSeconds = tokenLifetime;
+ this.algorithm = this.createAlgorithm(alg, secret, pubkey);
+ }
+
+ private Algorithm createAlgorithm(String alg, String secret, String pubkey)
+ throws IllegalArgumentException, IOException {
+ if (alg == null) {
+ alg = Config.TOKENALG_HS256;
+ }
+ switch (alg) {
+ case Config.TOKENALG_HS256:
+ return Algorithm.HMAC256(secret);
+ case Config.TOKENALG_RS256:
+ return Algorithm.RSA256(RSAKeyReader.getPublicKey(pubkey), RSAKeyReader.getPrivateKey(secret));
+ case Config.TOKENALG_RS512:
+ return Algorithm.RSA512(RSAKeyReader.getPublicKey(pubkey), RSAKeyReader.getPrivateKey(secret));
+ case Config.TOKENALG_CLIENT_RS256:
+ return Algorithm.RSA256(RSAKeyReader.getPublicKey(pubkey), null);
+ case Config.TOKENALG_CLIENT_RS512:
+ return Algorithm.RSA512(RSAKeyReader.getPublicKey(pubkey), null);
+ }
+ throw new IllegalArgumentException(String.format("unable to find algorithm for %s", alg));
+
+ }
+
+ public BearerToken createNewJWT(UserTokenPayload data) {
+ final String token = JWT.create().withIssuer(issuer).withExpiresAt(new Date(data.getExp()))
+ .withIssuedAt(new Date(data.getIat())).withSubject(data.getPreferredUsername())
+ .withClaim(NAME_CLAIM, data.getGivenName()).withClaim(FAMILYNAME_CLAIM, data.getFamilyName())
+ .withClaim(PROVIDERID_CLAIM, data.getProviderId())
+ .withArrayClaim(ROLES_CLAIM, data.getRoles().toArray(new String[data.getRoles().size()]))
+ .sign(this.algorithm);
+ LOG.trace("token created: {}", token);
+ return new BearerToken(token);
+ }
+
+ public DecodedJWT verify(String token) {
+ DecodedJWT jwt = null;
+ LOG.debug("try to verify token {}", token);
+ try {
+ JWTVerifier verifier = JWT.require(this.algorithm).withIssuer(issuer).build();
+ jwt = verifier.verify(token);
+
+ } catch (JWTVerificationException e) {
+ LOG.warn("unable to verify token {}:", token, e);
+ }
+ return jwt;
+ }
+
+ public long getDefaultExp() {
+ return new Date().getTime() + (this.tokenLifetimeSeconds * 1000);
+ }
+
+ public long getDefaultExp(long expIn) {
+ return new Date().getTime() + expIn;
+ }
+
+ public long getDefaultIat() {
+ return new Date().getTime();
+ }
+
+ public String getBearerToken(HttpServletRequest req) {
+ return this.getBearerToken(req, false);
+ }
+
+ public String getBearerToken(HttpServletRequest req, boolean checkCookie) {
+ final String authHeader = req.getHeader("Authorization");
+ if ((authHeader == null || !authHeader.startsWith("Bearer")) && checkCookie) {
+ Cookie[] cookies = req.getCookies();
+ Optional<Cookie> ocookie = Optional.empty();
+ if (cookies != null) {
+ ocookie = Arrays.stream(cookies).filter(c -> c != null && COOKIE_NAME_AUTH.equals(c.getName()))
+ .findFirst();
+ }
+ if (ocookie.isEmpty()) {
+ return null;
+ }
+ return ocookie.get().getValue();
+ }
+ return authHeader.substring(7);
+ }
+
+ public UserTokenPayload decode(HttpServletRequest req) throws JWTDecodeException {
+ final String token = this.getBearerToken(req);
+ return token != null ? this.decode(token) : null;
+ }
+
+ public UserTokenPayload decode(String token) {
+ if (token == null) {
+ return null;
+ }
+ DecodedJWT jwt = JWT.decode(token);
+ UserTokenPayload data = new UserTokenPayload();
+ data.setRoles(Arrays.asList(jwt.getClaim(ROLES_CLAIM).asArray(String.class)));
+ data.setExp(jwt.getExpiresAt().getTime());
+ data.setFamilyName(jwt.getClaim(FAMILYNAME_CLAIM).asString());
+ data.setGivenName(jwt.getClaim(NAME_CLAIM).asString());
+ data.setPreferredUsername(jwt.getClaim(NAME_CLAIM).asString());
+ data.setProviderId(jwt.getClaim(PROVIDERID_CLAIM).asString());
+ return data;
+ }
+
+ public Cookie createAuthCookie(BearerToken data) {
+ Cookie cookie = new Cookie(COOKIE_NAME_AUTH, data.getToken());
+ cookie.setMaxAge((int) this.tokenLifetimeSeconds);
+ cookie.setPath("/");
+ cookie.setHttpOnly(true);
+ cookie.setSecure(true);
+ return cookie;
+ }
+}