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);