[UI] Encrypt passwords in db 49/1649/1
authorIoakeim Samaras <ioakeim.samaras@ericsson.com>
Wed, 25 Sep 2019 12:35:35 +0000 (15:35 +0300)
committerIoakeim Samaras <ioakeim.samaras@ericsson.com>
Wed, 25 Sep 2019 12:35:35 +0000 (15:35 +0300)
The passwords stored in database are encrypted.
In this way, this data cannot be used by individuals
that shouldn't have access to it.

JIRA: VAL-55

Signed-off-by: Ioakeim Samaras <ioakeim.samaras@ericsson.com>
Change-Id: I3eb3b6a23398d37faa59938f84368711e7666978

docker/README.rst
docker/mariadb/deploy.sh
docker/ui/deploy.sh
ui/CHANGELOG.md
ui/README.rst
ui/db-scripts/EcompSdkDDLMySql_2_4_Common.sql
ui/pom.xml
ui/src/main/java/org/akraino/validation/ui/login/LoginStrategyImpl.java
ui/src/main/java/org/akraino/validation/ui/service/DbAdapter.java

index 80451d1..a86db5d 100644 (file)
@@ -108,6 +108,7 @@ NAME, name of the mariadb image, default value is validation
 TAG_PRE, first part of the image version, default value is mariadb
 TAG_VER, last part of the image version, default value is latest
 MARIADB_HOST_PORT, port on which mariadb is exposed on host, default value is 3307
+ENCRYPTION_KEY, the key that should be used by the AES algorithm for encrypting passwords stored in database, this variable is required
 
 In order to deploy the container, this script can be executed with the appropriate parameters.
 
@@ -116,7 +117,7 @@ Example (assuming the default variables have been utilized for building the imag
 .. code-block:: console
 
     cd validation/docker/mariadb
-    ./deploy.sh MARIADB_ROOT_PASSWORD=root_password MARIADB_AKRAINO_PASSWORD=akraino_password UI_ADMIN_PASSWORD=admin UI_AKRAINO_PASSWORD=akraino
+    ./deploy.sh MARIADB_ROOT_PASSWORD=root_password MARIADB_AKRAINO_PASSWORD=akraino_password UI_ADMIN_PASSWORD=admin UI_AKRAINO_PASSWORD=akraino ENCRYPTION_KEY=key
 
 Also, in order to re-deploy the database (it is assumed that the corresponding mariadb container has been stopped and deleted) while the persistent storage already exists (currently, the directory /var/lib/mariadb of the host is used), a different approach should be used after the image build process.
 
@@ -176,6 +177,7 @@ JENKINS_JOB_NAME, the name of Jenkins job capable of executing the blueprint val
 NEXUS_PROXY, the needed proxy in order for the Nexus server to be reachable, default value is none
 JENKINS_PROXY, the needed proxy in order for the Jenkins server to be reachable, default value is none
 CERTDIR, the directory where the SSL certificates can be found, default value is the working directory where self signed certificates exist only for demo purposes
+ENCRYPTION_KEY, the key that should be used by the AES algorithm for encrypting passwords stored in database, this variable is required
 
 Note that, for a functional UI, the following prerequisites are needed:
 
@@ -192,7 +194,7 @@ Example (assuming the default variables have been utilized for building the imag
 .. code-block:: console
 
     cd validation/docker/ui
-    ./deploy.sh DB_IP_PORT=172.17.0.3:3306 MARIADB_AKRAINO_PASSWORD=akraino_password
+    ./deploy.sh DB_IP_PORT=172.17.0.3:3306 MARIADB_AKRAINO_PASSWORD=akraino_password ENCRYPTION_KEY=key
 
 The kube-conformance container
 ==============================
index ea48b54..b3fea76 100755 (executable)
@@ -26,6 +26,7 @@ MARIADB_ROOT_PASSWORD=""
 MARIADB_AKRAINO_PASSWORD=""
 UI_ADMIN_PASSWORD=""
 UI_AKRAINO_PASSWORD=""
+ENCRYPTION_KEY=""
 # Image data
 REGISTRY=akraino
 NAME=validation
@@ -48,6 +49,7 @@ do
             MARIADB_HOST_PORT)    MARIADB_HOST_PORT=${VALUE} ;;
             UI_ADMIN_PASSWORD)    UI_ADMIN_PASSWORD=${VALUE} ;;
             UI_AKRAINO_PASSWORD)    UI_AKRAINO_PASSWORD=${VALUE} ;;
+            ENCRYPTION_KEY)    ENCRYPTION_KEY=${VALUE} ;;
             *)
     esac
 done
@@ -76,8 +78,14 @@ if [ -z "$UI_AKRAINO_PASSWORD" ]
     exit 1
 fi
 
+if [ -z "$ENCRYPTION_KEY" ]
+  then
+    echo "ERROR: You must specify the encryption key"
+    exit 1
+fi
+
 IMAGE="$REGISTRY"/"$NAME":"$TAG_PRE"-"$TAG_VER"
 chmod 0444 "/$(pwd)/mariadb.conf"
-docker run --detach --name $CONTAINER_NAME --publish $MARIADB_HOST_PORT:3306 -v $DOCKER_VOLUME_NAME:/var/lib/mysql -v "/$(pwd)/mariadb.conf:/etc/mysql/conf.d/my.cnf" -e MYSQL_ROOT_PASSWORD="$MARIADB_ROOT_PASSWORD" -e MYSQL_DATABASE="akraino" -e MYSQL_USER="akraino" -e MYSQL_PASSWORD="$MARIADB_AKRAINO_PASSWORD" -e UI_ADMIN_PASSWORD="$UI_ADMIN_PASSWORD" -e UI_AKRAINO_PASSWORD="$UI_AKRAINO_PASSWORD" $IMAGE
-docker exec $CONTAINER_NAME /bin/bash -c 'sed -i 's/admin_password/'"$UI_ADMIN_PASSWORD"'/g' /docker-entrypoint-initdb.d/EcompSdkDMLMySql_2_4_OS.sql ; sed -i 's/akraino_password/'"$UI_AKRAINO_PASSWORD"'/g' /docker-entrypoint-initdb.d/EcompSdkDMLMySql_2_4_OS.sql; continue=`ps aux | grep mysql` ; while [ -z "$continue" ]; do continue=`ps aux | grep mysql`; sleep 5; done ; sleep 10 ;'
+docker run --detach --name $CONTAINER_NAME --publish $MARIADB_HOST_PORT:3306 -v $DOCKER_VOLUME_NAME:/var/lib/mysql -v "/$(pwd)/mariadb.conf:/etc/mysql/conf.d/my.cnf" -e MYSQL_ROOT_PASSWORD="$MARIADB_ROOT_PASSWORD" -e MYSQL_DATABASE="akraino" -e MYSQL_USER="akraino" -e MYSQL_PASSWORD="$MARIADB_AKRAINO_PASSWORD" -e UI_ADMIN_PASSWORD="$UI_ADMIN_PASSWORD" -e UI_AKRAINO_PASSWORD="$UI_AKRAINO_PASSWORD" -e ENCRYPTION_KEY="$ENCRYPTION_KEY" $IMAGE
+docker exec $CONTAINER_NAME /bin/bash -c 'sed -i 's/admin_password/'"$UI_ADMIN_PASSWORD"'/g' /docker-entrypoint-initdb.d/EcompSdkDMLMySql_2_4_OS.sql ; sed -i 's/akraino_password/'"$UI_AKRAINO_PASSWORD"'/g' /docker-entrypoint-initdb.d/EcompSdkDMLMySql_2_4_OS.sql; echo "UPDATE fn_user SET LOGIN_PWD = HEX(AES_ENCRYPT(LOGIN_PWD, \"$ENCRYPTION_KEY\"))" >> /docker-entrypoint-initdb.d/EcompSdkDMLMySql_2_4_OS.sql ;continue=`ps aux | grep mysql` ; while [ -z "$continue" ]; do continue=`ps aux | grep mysql`; sleep 5; done ; sleep 10 ;'
 sleep 10
index 388cb9c..43bd5bc 100755 (executable)
@@ -33,6 +33,7 @@ DB_IP_PORT=""
 NEXUS_PROXY=""
 JENKINS_PROXY=""
 CERTDIR=$(pwd)
+ENCRYPTION_KEY=""
 
 for ARGUMENT in "$@"
 do
@@ -53,6 +54,7 @@ do
             NEXUS_PROXY) NEXUS_PROXY=${VALUE} ;;
             JENKINS_PROXY) JENKINS_PROXY=${VALUE} ;;
             CERTDIR) CERTDIR=${VALUE} ;;
+            ENCRYPTION_KEY) ENCRYPTION_KEY=${VALUE} ;;
             *)
     esac
 done
@@ -69,6 +71,12 @@ if [ -z "$MARIADB_AKRAINO_PASSWORD" ]
     exit 1
 fi
 
+if [ -z "$ENCRYPTION_KEY" ]
+  then
+    echo "ERROR: You must specify the encryption key"
+    exit 1
+fi
+
 IMAGE="$REGISTRY"/"$NAME":"$TAG_PRE"-"$TAG_VER"
-docker run --detach --name $CONTAINER_NAME --network="host" -v "$(pwd)/server.xml:/usr/local/tomcat/conf/server.xml" -v "$CERTDIR/bluval.key:/usr/local/tomcat/bluval.key" -v "$CERTDIR/bluval.crt:/usr/local/tomcat/bluval.crt" -v "$(pwd)/root_index.jsp:/usr/local/tomcat/webapps/ROOT/index.jsp" -e DB_IP_PORT="$DB_IP_PORT" -e MARIADB_AKRAINO_PASSWORD="$MARIADB_AKRAINO_PASSWORD" -e JENKINS_URL="$JENKINS_URL" -e JENKINS_USERNAME="$JENKINS_USERNAME" -e JENKINS_USER_PASSWORD="$JENKINS_USER_PASSWORD" -e JENKINS_JOB_NAME="$JENKINS_JOB_NAME" -e NEXUS_PROXY="$NEXUS_PROXY" -e JENKINS_PROXY="$JENKINS_PROXY" $IMAGE
+docker run --detach --name $CONTAINER_NAME --network="host" -v "$(pwd)/server.xml:/usr/local/tomcat/conf/server.xml" -v "$CERTDIR/bluval.key:/usr/local/tomcat/bluval.key" -v "$CERTDIR/bluval.crt:/usr/local/tomcat/bluval.crt" -v "$(pwd)/root_index.jsp:/usr/local/tomcat/webapps/ROOT/index.jsp" -e DB_IP_PORT="$DB_IP_PORT" -e MARIADB_AKRAINO_PASSWORD="$MARIADB_AKRAINO_PASSWORD" -e JENKINS_URL="$JENKINS_URL" -e JENKINS_USERNAME="$JENKINS_USERNAME" -e JENKINS_USER_PASSWORD="$JENKINS_USER_PASSWORD" -e JENKINS_JOB_NAME="$JENKINS_JOB_NAME" -e NEXUS_PROXY="$NEXUS_PROXY" -e JENKINS_PROXY="$JENKINS_PROXY" -e ENCRYPTION_KEY="$ENCRYPTION_KEY" $IMAGE
 sleep 10
index c1343fe..50d234a 100644 (file)
@@ -182,3 +182,11 @@ All notable changes to this project will be documented in this file.
 
 ### Removed
 
+## [0.3.3-SNAPSHOT] - 25 September 2019
+### Added
+- Encryption of passwords stored in database.
+
+### Changed
+- Password of users that try to login is taken into account
+
+### Removed
index 41b9a16..33ecb23 100644 (file)
@@ -177,6 +177,7 @@ NAME, name of the mariadb image, default value is validation
 TAG_PRE, first part of the image version, default value is mariadb
 TAG_VER, last part of the image version, default value is latest
 MARIADB_HOST_PORT, port on which mariadb is exposed on host, default value is 3307
+ENCRYPTION_KEY, the key that should be used by the AES algorithm for encrypting passwords stored in database, this variable is required
 
 Currently, two users are supported by the UI, namely admin (full privileges) and akraino (limited privileges). Their passwords must be defined in the database.
 
@@ -189,7 +190,7 @@ The mariadb root password, mariadb akraino user password (currently the UI conne
     cd validation/ui
     mvn docker:build -Ddocker.filter=akraino/validation:dev-mariadb-latest
     cd ../docker/mariadb
-    ./deploy.sh TAG_PRE=dev-mariadb MARIADB_ROOT_PASSWORD=<mariadb root user password> MARIADB_AKRAINO_PASSWORD=<mariadb akraino user password> UI_ADMIN_PASSWORD=<UI admin user password> UI_AKRAINO_PASSWORD=<UI akraino user password>
+    ./deploy.sh TAG_PRE=dev-mariadb MARIADB_ROOT_PASSWORD=<mariadb root user password> MARIADB_AKRAINO_PASSWORD=<mariadb akraino user password> UI_ADMIN_PASSWORD=<UI admin user password> UI_AKRAINO_PASSWORD=<UI akraino user password> ENCRYPTION_KEY=<encryption key>
     mysql -p<MARIADB_AKRAINO_PASSWORD> -uakraino -h <IP of the mariadb container> < ../../ui/db-scripts/examples/initialize_db_example.sql
 
 In order to retrieve the IP of the mariadb container, the following command should be executed:
@@ -409,6 +410,7 @@ JENKINS_JOB_NAME, the name of Jenkins job capable of executing the blueprint val
 NEXUS_PROXY, the needed proxy in order for the Nexus server to be reachable, default value is none
 JENKINS_PROXY, the needed proxy in order for the Jenkins server to be reachable, default value is none
 CERTDIR, the directory where the SSL certificates can be found, default value is the working directory where self signed certificates exist only for demo purposes
+ENCRYPTION_KEY, the key that should be used by the AES algorithm for encrypting passwords stored in database, this variable is required
 
 So, for a functional UI, the following prerequisites are needed:
 
@@ -420,9 +422,9 @@ Then, the following commands can be executed in order to deploy the UI container
 
 .. code-block:: console
     cd ../docker/ui
-    ./deploy.sh TAG_PRE=dev-ui DB_IP_PORT=<IP and port of the mariadb> MARIADB_AKRAINO_PASSWORD=<mariadb akraino password>
+    ./deploy.sh TAG_PRE=dev-ui DB_IP_PORT=<IP and port of the mariadb> MARIADB_AKRAINO_PASSWORD=<mariadb akraino password> ENCRYPTION_KEY=<encryption key>
 
-The content of the DB_IP_PORT can be for example '172.17.0.3:3306'.
+The content of the DB_IP_PORT can be for example '172.17.0.3:3306'. Also, the value of the encryption key should be the same as the value of the encryption key used in database deployment.
 
 Furthermore, the TAG_PRE variable should be defined as the default value is 'ui' (note that the 'dev-ui' is used for development purposes - look at pom.xml file).
 
@@ -430,7 +432,7 @@ If no proxy exists, the proxy ip and port variables should not be defined.
 
 The UI should be available in the following url:
 
-    https://localhost:8443/bluvalui/
+    https://<IP of UI container>:8443/bluvalui/
 
 Note that the deployment uses the network host mode, so the ports 8080 and 8443 must be available on the host.
 
index bdfb647..ebc838f 100644 (file)
@@ -311,8 +311,8 @@ create table fn_user (
     HRID CHARACTER VARYING(20),
     ORG_USER_ID CHARACTER VARYING(20),
     ORG_CODE CHARACTER VARYING(30),
-    LOGIN_ID CHARACTER VARYING(25),
-    LOGIN_PWD CHARACTER VARYING(25),
+    LOGIN_ID CHARACTER VARYING(255),
+    LOGIN_PWD CHARACTER VARYING(255),
     LAST_LOGIN_DATE TIMESTAMP,
     ACTIVE_YN CHARACTER VARYING(1) DEFAULT 'Y' NOT NULL,
     CREATED_ID INT(11),
index 6cdbc38..614d2ff 100644 (file)
@@ -14,7 +14,7 @@
 
     <groupId>org.akraino.validation</groupId>
     <artifactId>ui</artifactId>
-    <version>0.3.2-SNAPSHOT</version>
+    <version>0.3.3-SNAPSHOT</version>
     <name>Bluval UI Maven Webapp</name>
     <packaging>war</packaging>
 
index 55960ac..b6a78fc 100644 (file)
@@ -1,51 +1,41 @@
-/*-
- * ============LICENSE_START==========================================
- * ONAP Portal
- * ===================================================================
- * Copyright Â© 2017 AT&T Intellectual Property. All rights reserved.
- * ===================================================================
+/*
+ * Copyright (c) 2019 AT&T Intellectual Property. All rights reserved.
  *
- * Unless otherwise specified, all software contained herein is licensed
- * under the Apache License, Version 2.0 (the "License");
- * you may not use this software except in compliance with the License.
- * You may obtain a copy of the License at
+ * 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
+ * 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.
- *
- * Unless otherwise specified, all documentation contained herein is licensed
- * under the Creative Commons License, Attribution 4.0 Intl. (the "License");
- * you may not use this documentation except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *             https://creativecommons.org/licenses/by/4.0/
- *
- * Unless required by applicable law or agreed to in writing, documentation
- * 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============================================
- *
- *
+ * 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.
  */
 
 package org.akraino.validation.ui.login;
 
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.commons.codec.binary.Hex;
 import org.onap.portalsdk.core.auth.LoginStrategy;
 import org.onap.portalsdk.core.command.LoginBean;
 import org.onap.portalsdk.core.domain.RoleFunction;
@@ -62,9 +52,8 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.servlet.ModelAndView;
 
 /**
- * Implements basic single-signon login strategy for open-source
- * applications when users start at Portal. Extracts an encrypted user ID
- * sent by Portal.
+ * Implements basic single-signon login strategy for open-source applications
+ * when users start at Portal. Extracts an encrypted user ID sent by Portal.
  */
 public class LoginStrategyImpl extends LoginStrategy {
 
@@ -87,9 +76,11 @@ public class LoginStrategyImpl extends LoginStrategy {
         LoginBean commandBean = new LoginBean();
         String loginId = request.getParameter("loginId");
         String password = request.getParameter("password");
+        String key = System.getenv("ENCRYPTION_KEY");
+        password = aesEncrypt(password, key);
         commandBean.setLoginId(loginId);
         commandBean.setLoginPwd(password);
-        commandBean.setUserid(loginId);
+        // commandBean.setUserid(loginId);
         commandBean = loginService.findUser(commandBean,
                 (String) request.getAttribute(MenuProperties.MENU_PROPERTIES_FILENAME_KEY), new HashMap());
         List<RoleFunction> roleFunctionList = roleService.getRoleFunctions(loginId);
@@ -111,6 +102,40 @@ public class LoginStrategyImpl extends LoginStrategy {
         }
     }
 
+    @Override
+    public ModelAndView doExternalLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
+
+        invalidateExistingSession(request);
+
+        LoginBean commandBean = new LoginBean();
+        String loginId = request.getParameter("loginId");
+        String password = request.getParameter("password");
+        String key = System.getenv("ENCRYPTION_KEY");
+        password = aesEncrypt(password, key);
+        commandBean.setLoginId(loginId);
+        commandBean.setLoginPwd(password);
+        // commandBean.setUserid(loginId);
+        commandBean = loginService.findUser(commandBean,
+                (String) request.getAttribute(MenuProperties.MENU_PROPERTIES_FILENAME_KEY), new HashMap());
+        List<RoleFunction> roleFunctionList = roleService.getRoleFunctions(loginId);
+
+        if (commandBean.getUser() == null) {
+            String loginErrorMessage = (commandBean.getLoginErrorMessage() != null) ? commandBean.getLoginErrorMessage()
+                    : "login.error.external.invalid";
+            Map<String, String> model = new HashMap<>();
+            model.put("error", loginErrorMessage);
+            return new ModelAndView("login_external", "model", model);
+        } else {
+            // store the currently logged in user's information in the session
+            UserUtils.setUserSession(request, commandBean.getUser(), commandBean.getMenu(),
+                    commandBean.getBusinessDirectMenu(),
+                    SystemProperties.getProperty(SystemProperties.LOGIN_METHOD_BACKDOOR), roleFunctionList);
+            initateSessionMgtHandler(request);
+            // user has been authenticated, now take them to the welcome page
+            return new ModelAndView("redirect:welcome");
+        }
+    }
+
     @Override
     public String getUserId(HttpServletRequest request) throws PortalAPIException {
         // Check ECOMP Portal cookie
@@ -130,8 +155,8 @@ public class LoginStrategyImpl extends LoginStrategy {
     }
 
     /**
-     * Searches the request for the user-ID cookie and decrypts the value
-     * using a key configured in properties
+     * Searches the request for the user-ID cookie and decrypts the value using a
+     * key configured in properties
      *
      * @param request HttpServletRequest
      * @return User ID
@@ -154,7 +179,7 @@ public class LoginStrategyImpl extends LoginStrategy {
     /**
      * Searches the request for the named cookie.
      *
-     * @param request HttpServletRequest
+     * @param request    HttpServletRequest
      * @param cookieName Name of desired cookie
      * @return Cookie if found; otherwise null.
      */
@@ -167,4 +192,21 @@ public class LoginStrategyImpl extends LoginStrategy {
         return null;
     }
 
+    private String aesEncrypt(String password, String strKey) {
+        try {
+            byte[] keyBytes = Arrays.copyOf(strKey.getBytes("ASCII"), 16);
+            SecretKey key = new SecretKeySpec(keyBytes, "AES");
+            Cipher cipher = Cipher.getInstance("AES");
+            cipher.init(Cipher.ENCRYPT_MODE, key);
+            byte[] cleartext = password.getBytes("UTF-8");
+            byte[] ciphertextBytes = cipher.doFinal(cleartext);
+            return new String(Hex.encodeHex(ciphertextBytes));
+        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | UnsupportedEncodingException
+                | IllegalBlockSizeException | BadPaddingException e) {
+            LOGGER.error(EELFLoggerDelegate.errorLogger,
+                    "Error when encrypting password key" + UserUtils.getStackTrace(e));
+            return null;
+        }
+    }
+
 }
index 56eada6..756c03a 100644 (file)
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019 AT&T 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.
+ */
+
 package org.akraino.validation.ui.service;
 
 import java.io.IOException;