2  * ============LICENSE_START=======================================================
 
   3  * ONAP : ccsdk features
 
   4  * ================================================================================
 
   5  * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
 
   7  * ================================================================================
 
   8  * Licensed under the Apache License, Version 2.0 (the "License");
 
   9  * you may not use this file except in compliance with the License.
 
  10  * You may obtain a copy of the License at
 
  12  *     http://www.apache.org/licenses/LICENSE-2.0
 
  14  * Unless required by applicable law or agreed to in writing, software
 
  15  * distributed under the License is distributed on an "AS IS" BASIS,
 
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
  17  * See the License for the specific language governing permissions and
 
  18  * limitations under the License.
 
  19  * ============LICENSE_END=========================================================
 
  22 package org.onap.ccsdk.features.sdnr.wt.oauthprovider.data;
 
  24 import com.fasterxml.jackson.annotation.JsonGetter;
 
  25 import com.fasterxml.jackson.annotation.JsonIgnore;
 
  26 import com.fasterxml.jackson.annotation.JsonSetter;
 
  28 import java.io.FileNotFoundException;
 
  29 import java.io.IOException;
 
  30 import java.nio.file.Files;
 
  31 import java.security.SecureRandom;
 
  32 import java.util.Arrays;
 
  33 import java.util.List;
 
  34 import java.util.regex.Matcher;
 
  35 import java.util.regex.Pattern;
 
  36 import org.slf4j.Logger;
 
  37 import org.slf4j.LoggerFactory;
 
  41     private static final Logger LOG = LoggerFactory.getLogger(Config.class);
 
  42     private static final String DEFAULT_CONFIGFILENAME = "etc/oauth-provider.config.json";
 
  43     private static final String ENVVARIABLE = "${";
 
  44     private static final String REGEXENVVARIABLE = "(\\$\\{[A-Z0-9_-]+\\})";
 
  45     private static final Pattern pattern = Pattern.compile(REGEXENVVARIABLE);
 
  46     private static final String DEFAULT_TOKENISSUER = "Opendaylight";
 
  47     private static final String DEFAULT_TOKENSECRET = generateSecret();
 
  48     private static final String DEFAULT_REDIRECTURI = "/odlux/index.html#/oauth?token=";
 
  49     private static final String DEFAULT_SUPPORTODLUSERS = "true";
 
  50     public static final String TOKENALG_HS256 = "HS256";
 
  51     public static final String TOKENALG_RS256 = "RS256";
 
  52     public static final String TOKENALG_RS512 = "RS512";
 
  53     private static final String CLIENTALG_PRE = "Client";
 
  54     public static final String TOKENALG_CLIENT_RS256 = CLIENTALG_PRE + TOKENALG_RS256;
 
  55     public static final String TOKENALG_CLIENT_RS512 = CLIENTALG_PRE + TOKENALG_RS512;
 
  56     private static final String DEFAULT_TOKEN_ALGORITHM = TOKENALG_HS256;
 
  58     private static final long DEFAULT_TOKEN_LIFETIME = 30 * 60;
 
  59     private static final List<String> VALID_ALGORITHMS =
 
  60             Arrays.asList(TOKENALG_HS256, TOKENALG_RS256, TOKENALG_RS512, TOKENALG_CLIENT_RS256, TOKENALG_CLIENT_RS512);
 
  61     private static final List<String> VALID_ALGORITHMS_FOR_INTERNAL_LOGIN =
 
  62             Arrays.asList(TOKENALG_HS256, TOKENALG_RS256, TOKENALG_RS512);
 
  63     private static SecureRandom random;
 
  64     private static Config _instance;
 
  66     private List<OAuthProviderConfig> providers;
 
  67     private String redirectUri;
 
  68     private String supportOdlUsers;
 
  69     private String tokenSecret;
 
  70     private String tokenPubKey;
 
  71     private String algorithm;
 
  72     private String tokenIssuer;
 
  73     private String publicUrl;
 
  74     private long tokenLifetime;
 
  77     public String toString() {
 
  78         return "Config [providers=" + providers + ", redirectUri=" + redirectUri + ", supportOdlUsers="
 
  79                 + supportOdlUsers + ", tokenSecret=***, tokenPubKey=" + tokenPubKey + ", algorithm=" + algorithm
 
  80                 + ", tokenIssuer=" + tokenIssuer + ", publicUrl=" + publicUrl + ", tokenLifetime=" + tokenLifetime
 
  84     public List<OAuthProviderConfig> getProviders() {
 
  88     public void setProviders(List<OAuthProviderConfig> providers) {
 
  89         this.providers = providers;
 
  92     public String getRedirectUri() {
 
  96     public void setRedirectUri(String redirectUri) {
 
  97         this.redirectUri = redirectUri;
 
 100     public String getSupportOdlUsers() {
 
 101         return supportOdlUsers;
 
 104     public void setSupportOdlUsers(String supportOdlUsers) {
 
 105         this.supportOdlUsers = supportOdlUsers;
 
 108     public String getTokenSecret() {
 
 112     public void setTokenSecret(String tokenSecret) {
 
 113         this.tokenSecret = tokenSecret;
 
 116     public String getAlgorithm() {
 
 117         return this.algorithm;
 
 120     public void setAlgorithm(String alg) {
 
 121         this.algorithm = alg;
 
 124     @JsonGetter("tokenPubKey")
 
 125     public String getPublicKey() {
 
 126         return this.tokenPubKey;
 
 129     @JsonSetter("tokenPubKey")
 
 130     public void setPublicKey(String pubKey) {
 
 131         this.tokenPubKey = pubKey;
 
 134     public String getTokenIssuer() {
 
 138     public void setTokenIssuer(String tokenIssuer) {
 
 139         this.tokenIssuer = tokenIssuer;
 
 142     public String getPublicUrl() {
 
 146     public void setPublicUrl(String publicUrl) {
 
 147         this.publicUrl = publicUrl;
 
 150     public long getTokenLifetime() {
 
 151         return this.tokenLifetime;
 
 154     public void setTokenLifetime(long lifetime) {
 
 155         this.tokenLifetime = lifetime;
 
 159     private void handleEnvironmentVars() {
 
 160         if (isEnvExpression(this.tokenIssuer)) {
 
 161             this.tokenIssuer = getProperty(this.tokenIssuer, null);
 
 163         if (isEnvExpression(this.tokenSecret)) {
 
 164             this.tokenSecret = getProperty(this.tokenSecret, null);
 
 166         if (isEnvExpression(this.tokenPubKey)) {
 
 167             this.tokenPubKey = getProperty(this.tokenPubKey, null);
 
 169         if (isEnvExpression(this.algorithm)) {
 
 170             this.algorithm = getProperty(this.algorithm, null);
 
 172         if (isEnvExpression(this.publicUrl)) {
 
 173             this.publicUrl = getProperty(this.publicUrl, null);
 
 175         if (isEnvExpression(this.redirectUri)) {
 
 176             this.redirectUri = getProperty(this.redirectUri, null);
 
 178         if (isEnvExpression(this.supportOdlUsers)) {
 
 179             this.supportOdlUsers = getProperty(this.supportOdlUsers, null);
 
 181         if (this.providers != null && !this.providers.isEmpty()) {
 
 182             for (OAuthProviderConfig cfg : this.providers) {
 
 183                 cfg.handleEnvironmentVars();
 
 189     private void handleDefaultValues() {
 
 190         if (this.tokenIssuer == null || this.tokenIssuer.isEmpty()) {
 
 191             this.tokenIssuer = DEFAULT_TOKENISSUER;
 
 193         if (this.algorithm == null || this.algorithm.isEmpty()) {
 
 194             this.algorithm = DEFAULT_TOKEN_ALGORITHM;
 
 196         if (TOKENALG_HS256.equals(this.algorithm) && (this.tokenSecret == null || this.tokenSecret.isEmpty())) {
 
 197             this.tokenSecret = DEFAULT_TOKENSECRET;
 
 199         if (this.redirectUri == null || this.redirectUri.isEmpty() || "null".equals(this.redirectUri)) {
 
 200             this.redirectUri = DEFAULT_REDIRECTURI;
 
 202         if (this.publicUrl != null && (this.publicUrl.isEmpty() || "null".equals(this.publicUrl))) {
 
 203             this.publicUrl = null;
 
 205         if (this.supportOdlUsers == null || this.supportOdlUsers.isEmpty()) {
 
 206             this.supportOdlUsers = DEFAULT_SUPPORTODLUSERS;
 
 208         if (this.tokenLifetime <= 0) {
 
 209             this.tokenLifetime = DEFAULT_TOKEN_LIFETIME;
 
 213     static boolean isEnvExpression(String key) {
 
 214         return key != null && key.contains(ENVVARIABLE);
 
 217     public static String generateSecret() {
 
 218         return generateSecret(30);
 
 221     public static String generateSecret(int targetStringLength) {
 
 222         int leftLimit = 48; // numeral '0'
 
 223         int rightLimit = 122; // letter 'z'
 
 224         if (random == null) {
 
 225             random = new SecureRandom();
 
 227         String generatedString = random.ints(leftLimit, rightLimit + 1)
 
 228                 .filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97)).limit(targetStringLength)
 
 229                 .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString();
 
 230         return generatedString;
 
 235      * @param key environment var
 
 236      * @param defValue default value if no env var found
 
 239     public static String getProperty(final String key, final String defValue) {
 
 240         String value = defValue;
 
 241         //try to read env var
 
 242         boolean found = false;
 
 243         if (isEnvExpression(key)) {
 
 245             LOG.debug("try to find env var(s) for {}", key);
 
 246             final Matcher matcher = pattern.matcher(key);
 
 247             String tmp = new String(key);
 
 248             while (matcher.find() && matcher.groupCount() > 0) {
 
 249                 final String mkey = matcher.group(1);
 
 252                         LOG.debug("match found for v={} and env key={}", key, mkey);
 
 253                         String envvar = mkey.substring(2, mkey.length() - 1);
 
 254                         String env = System.getenv(envvar);
 
 255                         tmp = tmp.replace(mkey, env == null ? "" : env);
 
 256                         if (env != null && !env.isEmpty()) {
 
 259                     } catch (SecurityException e) {
 
 260                         LOG.warn("unable to read env {}: {}", key, e);
 
 271     public static boolean getPropertyBoolean(String key, boolean defaultValue) {
 
 272         final String value = getProperty(key, String.valueOf(defaultValue));
 
 273         return value.equals("true");
 
 276     public static Config load(String filename) throws IOException, InvalidConfigurationException {
 
 277         CustomObjectMapper mapper = new CustomObjectMapper();
 
 278         File file = new File(filename);
 
 279         if (!file.exists()) {
 
 280             throw new FileNotFoundException();
 
 282         String content = String.join("", Files.readAllLines(file.toPath()));
 
 283         Config cfg = mapper.readValue(content, Config.class);
 
 284         cfg.handleEnvironmentVars();
 
 285         cfg.handleDefaultValues();
 
 292     private void validate() throws InvalidConfigurationException {
 
 293         //verify that algorithm is supported
 
 294         if (!VALID_ALGORITHMS.contains(this.algorithm)) {
 
 295             throw new InvalidConfigurationException(String.format("Algorithm '%s' is not supported ", this.algorithm));
 
 297         //verify that set values are matching the algorithm
 
 298         //if hs256 check if secret is set
 
 299         if (this.algorithm.startsWith("HS")) {
 
 300             if (this.tokenSecret == null || this.tokenSecret.isBlank()) {
 
 301                 throw new InvalidConfigurationException(
 
 302                         String.format("There is no secret set for algorithm '%s'", this.algorithm));
 
 305         //if rs256 or rs512 check if secret(private key) and pubkey are set
 
 306         if (this.algorithm.startsWith("RS")) {
 
 307             if (this.tokenSecret == null || this.tokenSecret.isBlank()) {
 
 308                 throw new InvalidConfigurationException(
 
 309                         String.format("There is no secret set for algorithm '%s'", this.algorithm));
 
 311             if (this.tokenPubKey == null || this.tokenPubKey.isBlank()) {
 
 312                 throw new InvalidConfigurationException(
 
 313                         String.format("There is no public key for algorithm '%s'", this.algorithm));
 
 316         //if client rs256 or client rs512 check if pubkey are set
 
 317         if (this.algorithm.startsWith("Client")) {
 
 318             if (this.tokenPubKey == null || this.tokenPubKey.isBlank()) {
 
 319                 throw new InvalidConfigurationException(
 
 320                         String.format("There is no public key for algorithm '%s'", this.algorithm));
 
 326     public boolean doSupportOdlUsers() {
 
 327         return "true".equals(this.supportOdlUsers);
 
 331     public static Config getInstance() throws IOException, InvalidConfigurationException {
 
 332         return getInstance(DEFAULT_CONFIGFILENAME);
 
 335     public static Config getInstance(String filename) throws IOException, InvalidConfigurationException {
 
 336         if (_instance == null) {
 
 337             _instance = load(filename);
 
 342     public boolean loginActive() {
 
 343         return VALID_ALGORITHMS_FOR_INTERNAL_LOGIN.contains(this.algorithm);