diff --git a/docker/Makefile b/docker/Makefile index 1fed930604a..71792f94d2b 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -58,17 +58,17 @@ deploy-dev: deploy-local: # generate self-signed certificates - docker run --rm -t -v "$(BASE_DIR):/athenz" --user "$(shell id -u):$(shell id -g)" abvaidya/athenz-setup-env:1.9.22 sh /athenz/docker/setup-scripts/self-signed-certificates.sh + docker run --rm -t -v "$(BASE_DIR):/athenz" --user "$(shell id -u):$(shell id -g)" athenz/athenz-setup-env:1.9.22 sh /athenz/docker/setup-scripts/self-signed-certificates.sh # ZMS - docker run --rm -t -v "$(BASE_DIR):/athenz" --user "$(shell id -u):$(shell id -g)" abvaidya/athenz-setup-env:1.9.22 sh /athenz/docker/setup-scripts/zms-auto-config.sh + docker run --rm -t -v "$(BASE_DIR):/athenz" --user "$(shell id -u):$(shell id -g)" athenz/athenz-setup-env:1.9.22 sh /athenz/docker/setup-scripts/zms-auto-config.sh sh "./deploy-scripts/zms-deploy-local.sh" - docker run --rm -t --network="$(DOCKER_NETWORK)" -v "$(BASE_DIR):/athenz" --user "$(shell id -u):$(shell id -g)" abvaidya/athenz-setup-env:1.9.22 sh /athenz/docker/deploy-scripts/zms-debug.sh + docker run --rm -t --network="$(DOCKER_NETWORK)" -v "$(BASE_DIR):/athenz" --user "$(shell id -u):$(shell id -g)" athenz/athenz-setup-env:1.9.22 sh /athenz/docker/deploy-scripts/zms-debug.sh # ZTS - docker run --rm -t --network="$(DOCKER_NETWORK)" -v "$(BASE_DIR):/athenz" --user "$(shell id -u):$(shell id -g)" abvaidya/athenz-setup-env:1.9.22 sh /athenz/docker/setup-scripts/zts-auto-config.sh + docker run --rm -t --network="$(DOCKER_NETWORK)" -v "$(BASE_DIR):/athenz" --user "$(shell id -u):$(shell id -g)" athenz/athenz-setup-env:1.9.22 sh /athenz/docker/setup-scripts/zts-auto-config.sh sh "./deploy-scripts/zts-deploy-local.sh" - docker run --rm -t --network="$(DOCKER_NETWORK)" -v "$(BASE_DIR):/athenz" --user "$(shell id -u):$(shell id -g)" abvaidya/athenz-setup-env:1.9.22 sh /athenz/docker/deploy-scripts/zts-debug.sh + docker run --rm -t --network="$(DOCKER_NETWORK)" -v "$(BASE_DIR):/athenz" --user "$(shell id -u):$(shell id -g)" athenz/athenz-setup-env:1.9.22 sh /athenz/docker/deploy-scripts/zts-debug.sh # UI - docker run --rm -t --network="$(DOCKER_NETWORK)" -v "$(BASE_DIR):/athenz" --user "$(shell id -u):$(shell id -g)" abvaidya/athenz-setup-env:1.9.22 sh /athenz/docker/setup-scripts/ui-auto-config.sh + docker run --rm -t --network="$(DOCKER_NETWORK)" -v "$(BASE_DIR):/athenz" --user "$(shell id -u):$(shell id -g)" athenz/athenz-setup-env:1.9.22 sh /athenz/docker/setup-scripts/ui-auto-config.sh sh "./deploy-scripts/ui-deploy-local.sh" @@ -77,8 +77,8 @@ verify: docker run --rm -t --network="$(DOCKER_NETWORK)" -v "$(BASE_DIR):/athenz" --user "$(shell id -u):$(shell id -g)" athenz-setup-env sh /athenz/docker/deploy-scripts/zts-verify.sh verify-local: - docker run --rm -t --network="$(DOCKER_NETWORK)" -v "$(BASE_DIR):/athenz" --user "$(shell id -u):$(shell id -g)" abvaidya/athenz-setup-env:1.9.22 sh /athenz/docker/deploy-scripts/zms-verify.sh - docker run --rm -t --network="$(DOCKER_NETWORK)" -v "$(BASE_DIR):/athenz" --user "$(shell id -u):$(shell id -g)" abvaidya/athenz-setup-env:1.9.22 sh /athenz/docker/deploy-scripts/zts-verify.sh + docker run --rm -t --network="$(DOCKER_NETWORK)" -v "$(BASE_DIR):/athenz" --user "$(shell id -u):$(shell id -g)" athenz/athenz-setup-env:1.9.22 sh /athenz/docker/deploy-scripts/zms-verify.sh + docker run --rm -t --network="$(DOCKER_NETWORK)" -v "$(BASE_DIR):/athenz" --user "$(shell id -u):$(shell id -g)" athenz/athenz-setup-env:1.9.22 sh /athenz/docker/deploy-scripts/zts-verify.sh CONTAINERS := $(shell docker ps -aq --filter 'label=org.label-schema.url=https://www.athenz.io/') remove-all: remove-containers remove-networks remove-files reset-repo @@ -110,3 +110,11 @@ clean: remove-all docker image rm athenz-conf || true docker image rm athenz-setup-env || true docker image rm rdl-athenz-java-model || true + +remove-local-images: + docker image rm athenz/athenz-setup-env:1.9.22 || true + docker image rm athenz/athenz-zms-db:1.9.22 || true + docker image rm athenz/athenz-zms-server:1.9.22 || true + docker image rm athenz/athenz-zts-db:1.9.22 || true + docker image rm athenz/athenz-zts-server:1.9.22 || true + docker image rm athenz/athenz-ui:1.9.22 || true \ No newline at end of file diff --git a/docker/deploy-scripts/ui-deploy-local.sh b/docker/deploy-scripts/ui-deploy-local.sh index f21229c04b0..2ac66509c9f 100644 --- a/docker/deploy-scripts/ui-deploy-local.sh +++ b/docker/deploy-scripts/ui-deploy-local.sh @@ -52,12 +52,12 @@ docker run -d -h "${UI_HOST}" \ -e "PORT=${UI_CONTAINER_PORT}" \ -e "UI_CONF_PATH=/opt/athenz/ui/conf/ui_server" \ -e "ZMS_SERVER_URL=https://${ZMS_HOST}:${ZMS_PORT}/zms/v1/" \ - --name "${UI_HOST}" abvaidya/athenz-ui:1.9.22 + --name "${UI_HOST}" athenz/athenz-ui:1.9.22 # wait for UI to be ready until docker run --rm --entrypoint curl \ --network="${DOCKER_NETWORK}" \ --user "$(id -u):$(id -g)" \ - --name athenz-curl abvaidya/athenz-setup-env:1.9.22 \ + --name athenz-curl athenz/athenz-setup-env:1.9.22 \ -k --silent --fail --show-error --output /dev/null "https://${UI_HOST}:${UI_CONTAINER_PORT}/status" \ ; do echo 'UI is unavailable - will sleep 3s...' diff --git a/docker/deploy-scripts/zms-deploy-local.sh b/docker/deploy-scripts/zms-deploy-local.sh index 014767d0564..29468836ea3 100644 --- a/docker/deploy-scripts/zms-deploy-local.sh +++ b/docker/deploy-scripts/zms-deploy-local.sh @@ -55,7 +55,7 @@ docker run -d -h "${ZMS_DB_HOST}" \ --user mysql:mysql \ -v "${DOCKER_DIR}/db/zms/zms-db.cnf:/etc/mysql/conf.d/zms-db.cnf" \ -e "MYSQL_ROOT_PASSWORD=${ZMS_DB_ROOT_PASS}" \ - --name "${ZMS_DB_HOST}" abvaidya/athenz-zms-db:1.9.22 + --name "${ZMS_DB_HOST}" athenz/athenz-zms-db:1.9.22 # wait for ZMS DB to be ready docker run --rm \ --network="${DOCKER_NETWORK}" \ @@ -64,7 +64,7 @@ docker run --rm \ -v "${DOCKER_DIR}/db/zms/zms-db.cnf:/etc/my.cnf" \ -e "MYSQL_PWD=${ZMS_DB_ROOT_PASS}" \ --entrypoint '/bin/wait-for-mysql.sh' \ - --name wait-for-mysql abvaidya/athenz-zms-db:1.9.22 \ + --name wait-for-mysql athenz/athenz-zms-db:1.9.22 \ --user='root' \ --host="${ZMS_DB_HOST}" \ --port=3306 @@ -92,7 +92,7 @@ docker run -d -h "${ZMS_HOST}" \ -p "${ZMS_PORT}:${ZMS_PORT}" \ --dns="${DOCKER_DNS}" \ --network="${DOCKER_NETWORK}" \ - --user "athenz:shadow" \ + --user "$(id -u):$(id -g)" \ -v "${DOCKER_DIR}/zms/var:/opt/athenz/zms/var" \ -v "${DOCKER_DIR}/zms/conf:/opt/athenz/zms/conf/zms_server" \ -v "${DOCKER_DIR}/logs/zms:/opt/athenz/zms/logs/zms_server" \ @@ -103,12 +103,12 @@ docker run -d -h "${ZMS_HOST}" \ -e "ZMS_KEYSTORE_PASS=${ZMS_KEYSTORE_PASS}" \ -e "ZMS_TRUSTSTORE_PASS=${ZMS_TRUSTSTORE_PASS}" \ -e "ZMS_PORT=${ZMS_PORT}" \ - --name "${ZMS_HOST}" abvaidya/athenz-zms-server:1.9.22 + --name "${ZMS_HOST}" athenz/athenz-zms-server:1.9.22 # wait for ZMS to be ready until docker run --rm --entrypoint curl \ --network="${DOCKER_NETWORK}" \ --user "$(id -u):$(id -g)" \ - --name athenz-curl abvaidya/athenz-setup-env:1.9.22 \ + --name athenz-curl athenz/athenz-setup-env:1.9.22 \ -k --silent --fail --show-error --output /dev/null "https://${ZMS_HOST}:${ZMS_PORT}/zms/v1/status" \ ; do echo 'ZMS is unavailable - will sleep 3s...' diff --git a/docker/deploy-scripts/zts-deploy-local.sh b/docker/deploy-scripts/zts-deploy-local.sh index 6244d8e20de..eef7151179e 100644 --- a/docker/deploy-scripts/zts-deploy-local.sh +++ b/docker/deploy-scripts/zts-deploy-local.sh @@ -55,7 +55,7 @@ docker run -d -h "${ZTS_DB_HOST}" \ --user mysql:mysql \ -v "${DOCKER_DIR}/db/zts/zts-db.cnf:/etc/mysql/conf.d/zts-db.cnf" \ -e "MYSQL_ROOT_PASSWORD=${ZTS_DB_ROOT_PASS}" \ - --name "${ZTS_DB_HOST}" abvaidya/athenz-zts-db:1.9.22 + --name "${ZTS_DB_HOST}" athenz/athenz-zts-db:1.9.22 # wait for ZTS DB to be ready docker run --rm \ --network="${DOCKER_NETWORK}" \ @@ -64,7 +64,7 @@ docker run --rm \ -v "${DOCKER_DIR}/db/zts/zts-db.cnf:/etc/my.cnf" \ -e "MYSQL_PWD=${ZTS_DB_ROOT_PASS}" \ --entrypoint '/bin/wait-for-mysql.sh' \ - --name wait-for-mysql abvaidya/athenz-zts-db:1.9.22 \ + --name wait-for-mysql athenz/athenz-zts-db:1.9.22 \ --user='root' \ --host="${ZTS_DB_HOST}" \ --port=3306 @@ -106,12 +106,12 @@ docker run -d -h "${ZTS_HOST}" \ -e "ZMS_CLIENT_KEYSTORE_PASS=${ZMS_CLIENT_KEYSTORE_PASS}" \ -e "ZMS_CLIENT_TRUSTSTORE_PASS=${ZMS_CLIENT_TRUSTSTORE_PASS}" \ -e "ZTS_PORT=${ZTS_PORT}" \ - --name "${ZTS_HOST}" abvaidya/athenz-zts-server:1.9.22 + --name "${ZTS_HOST}" athenz/athenz-zts-server:1.9.22 # wait for ZTS to be ready until docker run --rm --entrypoint curl \ --network="${DOCKER_NETWORK}" \ --user "$(id -u):$(id -g)" \ - --name athenz-curl abvaidya/athenz-setup-env:1.9.22 \ + --name athenz-curl athenz/athenz-setup-env:1.9.22 \ -k --silent --fail --show-error --output /dev/null "https://${ZTS_HOST}:${ZTS_PORT}/zts/v1/status" \ ; do echo 'ZTS is unavailable - will sleep 3s...' diff --git a/docker/sample/CAs/create-self-signed-ca.sh b/docker/sample/CAs/create-self-signed-ca.sh index 0f85cce5773..df97a17f2f4 100755 --- a/docker/sample/CAs/create-self-signed-ca.sh +++ b/docker/sample/CAs/create-self-signed-ca.sh @@ -31,6 +31,9 @@ CN='Sample Self Signed Service CA' \ -keyout "${DEV_SERVICE_CA_KEY_PATH}" \ -out "${DEV_SERVICE_CA_PATH}" 2> /dev/null +# convert pem cert to der format so that it can be imported into OS ( optional step ) +openssl x509 -outform der -in "${DEV_ATHENZ_CA_PATH}" -out "${DEV_ATHENZ_CA_DER_PATH}" + # print result cat < /dev/null # sign request -openssl x509 -req -days 3650 \ +openssl x509 -req -days 30 \ -in "${DEV_DOMAIN_ADMIN_CSR_PATH}" \ -CA "${DEV_USER_CA_PATH}" \ -CAkey "${DEV_USER_CA_KEY_PATH}" \ diff --git a/docker/sample/env.dev.sh b/docker/sample/env.dev.sh index c485bedf77e..a33d449ffa9 100644 --- a/docker/sample/env.dev.sh +++ b/docker/sample/env.dev.sh @@ -17,6 +17,7 @@ export DEV_USER_CA_KEY_PATH="${DEV_CA_DIR}/user_ca.pem" export DEV_USER_CA_PATH="${DEV_CA_DIR}/user_ca.pem" export DEV_SERVICE_CA_KEY_PATH="${DEV_CA_DIR}/service_ca.pem" export DEV_SERVICE_CA_PATH="${DEV_CA_DIR}/service_ca.pem" +export DEV_ATHENZ_CA_DER_PATH="${DEV_CA_DIR}/athenz_ca.der" ### ---------------------------------------------------------------- ### domain admin diff --git a/docker/sample/ui/create-self-signed-certs.sh b/docker/sample/ui/create-self-signed-certs.sh index f27c97176d1..cfcee265b2d 100755 --- a/docker/sample/ui/create-self-signed-certs.sh +++ b/docker/sample/ui/create-self-signed-certs.sh @@ -17,7 +17,7 @@ CN='Sample Self Signed UI' SAN="${UI_HOST}" openssl req -nodes \ -keyout "${DEV_UI_CERT_KEY_PATH}" \ -out "${DEV_UI_CSR_PATH}" 2> /dev/null # sign request -SAN="${UI_HOST}" openssl x509 -req -days 3650 \ +SAN="${UI_HOST}" openssl x509 -req -days 364 \ -in "${DEV_UI_CSR_PATH}" \ -CA "${DEV_ATHENZ_CA_PATH}" \ -CAkey "${DEV_ATHENZ_CA_KEY_PATH}" \ diff --git a/docker/sample/zms/create-self-signed-certs.sh b/docker/sample/zms/create-self-signed-certs.sh index 908b0c221be..73bf8daa19f 100755 --- a/docker/sample/zms/create-self-signed-certs.sh +++ b/docker/sample/zms/create-self-signed-certs.sh @@ -15,7 +15,7 @@ CN='Sample Self Signed ZMS' SAN="${ZMS_HOST}" openssl req -nodes \ -keyout "${DEV_ZMS_CERT_KEY_PATH}" \ -out "${DEV_ZMS_CSR_PATH}" 2> /dev/null # sign request -SAN="${ZMS_HOST}" openssl x509 -req -days 3650 \ +SAN="${ZMS_HOST}" openssl x509 -req -days 364 \ -in "${DEV_ZMS_CSR_PATH}" \ -CA "${DEV_ATHENZ_CA_PATH}" \ -CAkey "${DEV_ATHENZ_CA_KEY_PATH}" \ diff --git a/docker/sample/zts/create-self-signed-certs.sh b/docker/sample/zts/create-self-signed-certs.sh index 5640f212419..51bdafeb01f 100755 --- a/docker/sample/zts/create-self-signed-certs.sh +++ b/docker/sample/zts/create-self-signed-certs.sh @@ -17,7 +17,7 @@ CN='Sample Self Signed ZTS' SAN="${ZTS_HOST}" openssl req -nodes \ -keyout "${DEV_ZTS_CERT_KEY_PATH}" \ -out "${DEV_ZTS_CSR_PATH}" 2> /dev/null # sign request -SAN="${ZTS_HOST}" openssl x509 -req -days 3650 \ +SAN="${ZTS_HOST}" openssl x509 -req -days 364 \ -in "${DEV_ZTS_CSR_PATH}" \ -CA "${DEV_ATHENZ_CA_PATH}" \ -CAkey "${DEV_ATHENZ_CA_KEY_PATH}" \ diff --git a/docker/setup-scripts/zms-auto-config.sh b/docker/setup-scripts/zms-auto-config.sh index 92eb6c707e3..6d9eadf4a2f 100755 --- a/docker/setup-scripts/zms-auto-config.sh +++ b/docker/setup-scripts/zms-auto-config.sh @@ -75,13 +75,15 @@ echo '6. config the Athenz domain admin' | colored_cat g echo "your setting: DOMAIN_ADMIN=${DOMAIN_ADMIN}" | colored_cat y sed -i "s/user\.github-.*$/${DOMAIN_ADMIN}/g" "${ZMS_CONF_DIR}/zms.properties" -echo '7. summary' | colored_cat g +echo '7. configure the necessary user authorities to work in local / test environment.' | colored_cat g +sed -i "s/athenz\.zms\.authority_classes=/athenz\.zms\.authority_classes=com\.yahoo\.athenz\.auth\.impl\.PrincipalAuthority,com\.yahoo\.athenz\.auth\.impl\.TestUserAuthority,/g" "${ZMS_CONF_DIR}/zms.properties" + +echo '8. summary' | colored_cat g tree "${CA_DIR}" tree "${PROD_ZMS_DIR}" tree "${ZMS_DIR}" - ### ---------------------------------------------------------------- echo '' echo '# Get Athenz domain admin user certificate for accessing ZMS' | colored_cat r diff --git a/docker/zms/Dockerfile b/docker/zms/Dockerfile index 73a9cced866..2a2a559e2a8 100644 --- a/docker/zms/Dockerfile +++ b/docker/zms/Dockerfile @@ -18,10 +18,6 @@ LABEL org.label-schema.vcs-ref=$VCS_REF ARG GID=1001 ARG UID=10001 -RUN apk update && apk add linux-pam - -RUN adduser -S -H -u 10002 uiuser && passwd -u uiuser << Athenz123# - # add athenz user RUN addgroup -g ${GID} athenz && \ adduser -S -D -H -s /sbin/nologin -u ${UID} -G athenz athenz diff --git a/docker/zms/conf/zms.properties b/docker/zms/conf/zms.properties index f8b19826263..4d2e6f3bde5 100644 --- a/docker/zms/conf/zms.properties +++ b/docker/zms/conf/zms.properties @@ -9,7 +9,7 @@ # Comma separated list of authority implementation classes to support # authenticating principals in ZMS -athenz.zms.authority_classes=com.yahoo.athenz.auth.impl.PrincipalAuthority,com.yahoo.athenz.auth.impl.UserAuthority,com.yahoo.athenz.auth.oauth.OAuthCertBoundJwtAccessTokenAuthority,com.yahoo.athenz.auth.impl.CertificateAuthority +athenz.zms.authority_classes=com.yahoo.athenz.auth.oauth.OAuthCertBoundJwtAccessTokenAuthority,com.yahoo.athenz.auth.impl.CertificateAuthority # Principal Authority class. If defined and the caller asks for the header # name for the getUserToken api, the header from this authority will be diff --git a/libs/java/auth_core/pom.xml b/libs/java/auth_core/pom.xml index 72f8629c894..38d5100b184 100644 --- a/libs/java/auth_core/pom.xml +++ b/libs/java/auth_core/pom.xml @@ -73,6 +73,12 @@ ${jjwt.version} runtime + + org.mockito + mockito-inline + ${mockito.version} + test + diff --git a/libs/java/auth_core/src/main/java/com/yahoo/athenz/auth/impl/TestUserAuthority.java b/libs/java/auth_core/src/main/java/com/yahoo/athenz/auth/impl/TestUserAuthority.java new file mode 100644 index 00000000000..3eaeacccd75 --- /dev/null +++ b/libs/java/auth_core/src/main/java/com/yahoo/athenz/auth/impl/TestUserAuthority.java @@ -0,0 +1,140 @@ +/* + * Copyright 2020 Verizon Media + * + * 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 com.yahoo.athenz.auth.impl; + +import com.yahoo.athenz.auth.Authority; +import com.yahoo.athenz.auth.Principal; +import org.bouncycastle.util.encoders.Base64; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.charset.StandardCharsets; + +/** + * Implementation to be used by local testing purposes. + * NOT RECOMMENDED to be used in production + */ +public class TestUserAuthority implements Authority { + + private static final Logger LOG = LoggerFactory.getLogger(TestUserAuthority.class); + public static final String ATHENZ_AUTH_CHALLENGE = "Basic realm=\"athenz\""; + + @Override + public void initialize() { + } + + @Override + public String getID() { + return "Auth-TESTUSER"; + } + + @Override + public String getDomain() { + return "user"; + } + + @Override + public String getHeader() { + return "Authorization"; + } + + @Override + public String getAuthenticateChallenge() { + return ATHENZ_AUTH_CHALLENGE; + } + + /* + * we don't want the user to keep specifying their username and + * password as part of the request. instead, the user must first + * request a usertoken and then use that usertoken for all other + * requests against ZMS and ZTS servers. + * @see com.yahoo.athenz.auth.Authority#allowAuthorization() + */ + @Override + public boolean allowAuthorization() { + return false; + } + + @Override + public Principal authenticate(String creds, String remoteAddr, String httpMethod, StringBuilder errMsg) { + errMsg = errMsg == null ? new StringBuilder(512) : errMsg; + + // the HTTP Basic authorization format is: Basic base64(:) + + if (!creds.startsWith("Basic ")) { + errMsg.append("UserAuthority:authenticate: credentials do not start with 'Basic '"); + LOG.error(errMsg.toString()); + return null; + } + + final String encodedPassword = creds.substring(6); + if (encodedPassword.isEmpty()) { + errMsg.append("TestUserAuthority:authenticate: no credentials after 'Basic '"); + LOG.error(errMsg.toString()); + return null; + } + + // decode - need to skip the first 6 bytes for 'Basic ' + + String decodedCreds; + try { + decodedCreds = new String(Base64.decode(encodedPassword.getBytes(StandardCharsets.UTF_8))); + } catch (Exception e) { + errMsg.append("UserAuthority:authenticate: factory exc=").append(e.getMessage()); + LOG.error(errMsg.toString()); + return null; + } + + int idx = decodedCreds.indexOf(':'); + if (idx == -1) { + errMsg.append("TestUserAuthority: authenticate: no password specified"); + LOG.error(errMsg.toString()); + return null; + } + + final String username = decodedCreds.substring(0, idx); + final String password = decodedCreds.substring(idx + 1); + + // just a simple check to make sure username and password match + if (!username.equals(password)) { + LOG.error("TestUserAuthority:authenticate: failed: username and password do not match"); + return null; + } + + if (LOG.isDebugEnabled()) { + LOG.debug("TestUserAuthority.authenticate: valid user={}", username); + } + + // all the role members in Athenz are normalized to lower case so we need to make + // sure our principal's name and domain are created with lower case as well + + long issueTime = 0; + SimplePrincipal princ = getSimplePrincipal(username.toLowerCase(), creds, issueTime); + if (princ == null) { + errMsg.append("TestUserAuthority:authenticate: failed to create principal: user=") + .append(username); + LOG.error(errMsg.toString()); + return null; + } + princ.setUnsignedCreds(username); + return princ; + } + + SimplePrincipal getSimplePrincipal(String name, String creds, long issueTime) { + return (SimplePrincipal) SimplePrincipal.create(getDomain(), + name, creds, issueTime, this); + } +} diff --git a/libs/java/auth_core/src/test/java/com/yahoo/athenz/auth/impl/TestUserAuthorityTest.java b/libs/java/auth_core/src/test/java/com/yahoo/athenz/auth/impl/TestUserAuthorityTest.java new file mode 100644 index 00000000000..8e64e040aee --- /dev/null +++ b/libs/java/auth_core/src/test/java/com/yahoo/athenz/auth/impl/TestUserAuthorityTest.java @@ -0,0 +1,124 @@ +/* + * Copyright 2020 Verizon Media + * + * 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 com.yahoo.athenz.auth.impl; + +import com.yahoo.athenz.auth.Principal; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.testng.annotations.Test; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.any; +import static org.testng.Assert.*; + +public class TestUserAuthorityTest { + + @Test + public void testGetID() { + TestUserAuthority tua = new TestUserAuthority(); + assertEquals(tua.getID(), "Auth-TESTUSER"); + } + + @Test + public void testGetDomain() { + TestUserAuthority tua = new TestUserAuthority(); + assertEquals(tua.getDomain(), "user"); + } + + @Test + public void testGetHeader() { + TestUserAuthority tua = new TestUserAuthority(); + assertEquals(tua.getHeader(), "Authorization"); + } + + @Test + public void testGetAuthenticateChallenge() { + TestUserAuthority tua = new TestUserAuthority(); + assertEquals(tua.getAuthenticateChallenge(), TestUserAuthority.ATHENZ_AUTH_CHALLENGE); + } + + @Test + public void testAllowAuthorization() { + TestUserAuthority tua = new TestUserAuthority(); + assertFalse(tua.allowAuthorization()); + } + + @Test + public void testAuthenticate() { + TestUserAuthority tua = new TestUserAuthority(); + StringBuilder errMsg = new StringBuilder(); + + try { + tua.initialize(); + }catch (Exception ex) { + fail(); + } + + // happy path + String testToken = "Basic dGVzdHVzZXI6dGVzdHVzZXI="; + Principal principal = tua.authenticate(testToken, "10.72.118.45", "GET", errMsg); + assertNotNull(principal); + + // username and password dont match + testToken = "Basic dGVzdHVzZXI6dGVzdHB3ZA=="; + principal = tua.authenticate(testToken, "10.72.118.45", "GET", errMsg); + assertNull(principal); + + // invalid format + testToken = "dGVzdHVzZXI6dGVzdHB3ZA=="; + principal = tua.authenticate(testToken, "10.72.118.45", "GET", errMsg); + assertNull(principal); + + // invalid format 2 + testToken = "Basic "; + principal = tua.authenticate(testToken, "10.72.118.45", "GET", errMsg); + assertNull(principal); + + // invalid format 3 + testToken = "Basic feeewa"; + principal = tua.authenticate(testToken, "10.72.118.45", "GET", errMsg); + assertNull(principal); + + // invalid format 4 + testToken = "Basic dGVzdHVzZXI6"; + principal = tua.authenticate(testToken, "10.72.118.45", "GET", errMsg); + assertNull(principal); + + // invalid format 5 + testToken = "Basic dGVzdHVzZXI="; + principal = tua.authenticate(testToken, "10.72.118.45", "GET", errMsg); + assertNull(principal); + + // Failed to create principal + try (MockedStatic theMock = Mockito.mockStatic(SimplePrincipal.class)) { + theMock.when((MockedStatic.Verification) SimplePrincipal.create(anyString(), anyString(), anyString(), anyLong(), any())).thenReturn(null); + testToken = "Basic dGVzdHVzZXI6dGVzdHVzZXI="; + principal = tua.authenticate(testToken, "10.72.118.45", "GET", errMsg); + assertNull(principal); + } + + } + + @Test + public void testGetSimplePrincipal() { + TestUserAuthority tua = new TestUserAuthority(); + long issueTime = System.currentTimeMillis(); + SimplePrincipal sp = tua.getSimplePrincipal("abc", "xyz", issueTime); + assertNotNull(sp); + assertEquals(sp.getAuthority().getClass(), TestUserAuthority.class); + } +} \ No newline at end of file diff --git a/start-local-athenz.sh b/start-local-athenz.sh new file mode 100755 index 00000000000..87f43b0d2a7 --- /dev/null +++ b/start-local-athenz.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +set -eu +set -o pipefail + +cd docker + +# import functions +. ./setup-scripts/common/color-print.sh + +make deploy-local & +MAKE_PID=$! + +wait $MAKE_PID + +cat <<'EOF' | colored_cat c + +################################################# +### Athenz is up! +################################################# + +EOF + +printf 'You can access UI now at https://localhost' | colored_cat g +printf '\n' +printf 'If you are on Mac you can run the following command to add the self-signed certificate to login keychain, so that browser does not complain about it. \n' | colored_cat y +printf 'security add-trusted-cert -k %s/Library/Keychains/login.keychain sample/CAs/athenz_ca.der\n' "$HOME" +printf '\n' \ No newline at end of file diff --git a/stop-local-athenz.sh b/stop-local-athenz.sh new file mode 100755 index 00000000000..20afd8630e6 --- /dev/null +++ b/stop-local-athenz.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +set -eu +set -o pipefail + +cd docker + +# import functions +. ./setup-scripts/common/color-print.sh + +# stop containers and remove generated files, local images +make remove-all + +cd .. + +printf '\n' +printf 'If you are on Mac you can run the following command to remove the self-signed certificate from login keychain. \n' | colored_cat y +printf 'security delete-certificate -c "Sample Self Signed Athenz CA" -t' +printf '\n' +printf 'If you want to clean up docker images please run the following command' | colored_cat y +printf '\n' +printf 'cd docker && make remove-local-images && cd..' +printf '\n' \ No newline at end of file diff --git a/ui/src/__tests__/server/handlers/api.test.js b/ui/src/__tests__/server/handlers/api.test.js index 93d7614f23b..3085c5387a7 100644 --- a/ui/src/__tests__/server/handlers/api.test.js +++ b/ui/src/__tests__/server/handlers/api.test.js @@ -22,6 +22,7 @@ const CLIENTS = require('../../../server/clients'); const config = { zms: 'https://zms.athenz.io', + zmsLoginUrl: 'https://zms.athenz.io', athenzDomainService: 'athenz.unit-test', headerLinks: [], allProviders: [ @@ -944,7 +945,7 @@ describe('Fetchr Server API Test', () => { .get('/api/v1/auth-options') .then((res) => { expect(res.body).toEqual({ - zms: 'https://zms.athenz.io', + zmsLoginUrl: 'https://zms.athenz.io', athenzDomainService: 'athenz.unit-test', }); }); diff --git a/ui/src/config/default-config.js b/ui/src/config/default-config.js index 72491e0f918..292b4667365 100644 --- a/ui/src/config/default-config.js +++ b/ui/src/config/default-config.js @@ -20,6 +20,8 @@ const config = { timeZone: 'America/Los_Angeles', language: 'en-US', zms: process.env.ZMS_SERVER_URL || 'https://localhost:4443/zms/v1/', + zmsLoginUrl: + process.env.ZMS_LOGIN_URL || 'https://localhost:4443/zms/v1/', authHeader: 'Athenz-Principal-Auth', strictSSL: false, user: 'ui-server', diff --git a/ui/src/pages/login.js b/ui/src/pages/login.js index 61c4eb96d1c..4fa9a5d4a4d 100644 --- a/ui/src/pages/login.js +++ b/ui/src/pages/login.js @@ -143,7 +143,7 @@ export default class PageLogin extends React.Component { this.state.username + ':' + this.state.password ).toString('base64'); fetch( - `${this.props.options.zms}user/_self_/token?services=${this.props.options.athenzDomainService}`, + `${this.props.options.zmsLoginUrl}user/_self_/token?services=${this.props.options.athenzDomainService}`, { headers: { Authorization: 'Basic ' + cred, diff --git a/ui/src/server/handlers/api.js b/ui/src/server/handlers/api.js index addd85f29d2..b28dce313be 100644 --- a/ui/src/server/handlers/api.js +++ b/ui/src/server/handlers/api.js @@ -631,7 +631,7 @@ Fetchr.registerService({ name: 'auth-options', read(req, resource, params, config, callback) { callback(null, { - zms: appConfig.zms, + zmsLoginUrl: appConfig.zmsLoginUrl, athenzDomainService: appConfig.athenzDomainService, }); }, @@ -992,16 +992,6 @@ Fetchr.registerService({ }, }); -Fetchr.registerService({ - name: 'auth-options', - read(req, resource, params, config, callback) { - callback(null, { - zms: appConfig.zms, - athenzDomainService: appConfig.athenzDomainService, - }); - }, -}); - Fetchr.registerService({ name: 'domain-history', read(req, resource, params, config, callback) { @@ -1055,6 +1045,7 @@ module.exports.load = function(config, secrets) { servicePageConfig: config.servicePageConfig, productMasterLink: config.productMasterLink, allPrefixes: config.allPrefixes, + zmsLoginUrl: config.zmsLoginUrl, }; return CLIENTS.load(config, secrets); };