diff --git a/fda-authentication-service/Dockerfile b/fda-authentication-service/Dockerfile index a68b28577fa9c76437921607e1a4a0706018c916..f687467497b42f78b264aa986e77ae7fdf47da92 100644 --- a/fda-authentication-service/Dockerfile +++ b/fda-authentication-service/Dockerfile @@ -16,12 +16,21 @@ RUN keytool -genkeypair -storepass password -storetype PKCS12 -keyalg RSA -keysi RUN /opt/keycloak/bin/kc.sh build ###### SECOND STAGE ###### +FROM redhat/ubi9-minimal as binary + +RUN microdnf update -y && microdnf install -y curl-minimal libcurl-minimal + +###### THIRD STAGE ###### FROM keycloak/keycloak:21.0 as runtime COPY --from=config /opt/keycloak/ /opt/keycloak/ +COPY --from=binary /usr/lib64 /usr/lib64 +COPY --from=binary /usr/bin/curl /usr/bin/curl USER root -COPY ./dbrepo-realm.json /dbrepo-realm.json +COPY ./dbrepo-realm.json /opt/keycloak/data/import/dbrepo-realm.json +COPY ./service-register.sh /app/service-register.sh +COPY ./docker-entrypoint.sh /app/docker-entrypoint.sh ENV METADATA_USERNAME=root ENV METADATA_PASSWORD=dbrepo @@ -32,10 +41,8 @@ ENV KC_DB_USERNAME=${METADATA_USERNAME} ENV KC_DB_PASSWORD=${METADATA_PASSWORD} ENV KC_HOSTNAME=localhost -ENV KEYCLOAK_IMPORT=/dbrepo-realm.json +ENV KEYCLOAK_IMPORT=/opt/keycloak/data/import/dbrepo-realm.json ENV KEYCLOAK_ADMIN=keycloak ENV KEYCLOAK_ADMIN_PASSWORD=keycloak -USER 1000 - -ENTRYPOINT ["/opt/keycloak/bin/kc.sh", "start"] +ENTRYPOINT ["bash", "/app/docker-entrypoint.sh"] diff --git a/fda-authentication-service/README.md b/fda-authentication-service/README.md new file mode 100644 index 0000000000000000000000000000000000000000..71ea75fc91dd2706f5576db7ff730a526097b566 --- /dev/null +++ b/fda-authentication-service/README.md @@ -0,0 +1,17 @@ +# Authentication Service + +Attention: self-enrollment is not possible anymore (Keycloak) + +## Create Users + +- Visit [localhost:8443](https://localhost:8443) and login with default admin credentials `keycloak:keycloak` +- Select realm `dbrepo` +- Visit `Users` -> `Create` and set a non-temporary password + +## API + +### Create Token + +```console +curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d '{"client_id":"dbrepo-client","username":"ABC","password":"XYZ","client_secret":"123","grant_type":"password","scope":"openid"}' +``` diff --git a/fda-authentication-service/dbrepo-realm.json b/fda-authentication-service/dbrepo-realm.json index b565bac8136c23e7081ecc7f2961bc71697cde24..1e71c9a8a5425a9f92a13285d9d1dc31720c48df 100644 --- a/fda-authentication-service/dbrepo-realm.json +++ b/fda-authentication-service/dbrepo-realm.json @@ -26,7 +26,7 @@ "oauth2DeviceCodeLifespan" : 600, "oauth2DevicePollingInterval" : 5, "enabled" : true, - "sslRequired" : "external", + "sslRequired" : "none", "registrationAllowed" : false, "registrationEmailAsUsername" : false, "rememberMe" : false, @@ -375,7 +375,7 @@ "otpPolicyLookAheadWindow" : 1, "otpPolicyPeriod" : 30, "otpPolicyCodeReusable" : false, - "otpSupportedApplications" : [ "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName", "totpAppFreeOTPName" ], + "otpSupportedApplications" : [ "totpAppFreeOTPName", "totpAppMicrosoftAuthenticatorName", "totpAppGoogleName" ], "webAuthnPolicyRpEntityName" : "keycloak", "webAuthnPolicySignatureAlgorithms" : [ "ES256" ], "webAuthnPolicyRpId" : "", @@ -495,7 +495,9 @@ "publicClient" : true, "frontchannelLogout" : false, "protocol" : "openid-connect", - "attributes" : { }, + "attributes" : { + "post.logout.redirect.uris" : "+" + }, "authenticationFlowBindingOverrides" : { }, "fullScopeAllowed" : false, "nodeReRegistrationTimeout" : 0, @@ -521,7 +523,9 @@ "publicClient" : false, "frontchannelLogout" : false, "protocol" : "openid-connect", - "attributes" : { }, + "attributes" : { + "post.logout.redirect.uris" : "+" + }, "authenticationFlowBindingOverrides" : { }, "fullScopeAllowed" : false, "nodeReRegistrationTimeout" : 0, @@ -564,6 +568,22 @@ "authenticationFlowBindingOverrides" : { }, "fullScopeAllowed" : true, "nodeReRegistrationTimeout" : -1, + "protocolMappers" : [ { + "id" : "6ff49409-9800-4d86-bee4-c8e88aaa313e", + "name" : "User Theme", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "aggregate.attrs" : "true", + "multivalued" : "false", + "userinfo.token.claim" : "true", + "user.attribute" : "theme_dark", + "id.token.claim" : "false", + "access.token.claim" : "true", + "claim.name" : "theme_dark" + } + } ], "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] }, { @@ -586,7 +606,9 @@ "publicClient" : false, "frontchannelLogout" : false, "protocol" : "openid-connect", - "attributes" : { }, + "attributes" : { + "post.logout.redirect.uris" : "+" + }, "authenticationFlowBindingOverrides" : { }, "fullScopeAllowed" : false, "nodeReRegistrationTimeout" : 0, @@ -656,7 +678,8 @@ "consentRequired" : false, "config" : { "id.token.claim" : "true", - "access.token.claim" : "true" + "access.token.claim" : "true", + "userinfo.token.claim" : "true" } } ] }, { @@ -928,6 +951,7 @@ "consentRequired" : false, "config" : { "multivalued" : "true", + "userinfo.token.claim" : "true", "user.attribute" : "foo", "id.token.claim" : "true", "access.token.claim" : "true", @@ -1178,7 +1202,7 @@ "subType" : "authenticated", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "oidc-full-name-mapper", "saml-user-property-mapper", "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "saml-role-list-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-full-name-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-user-attribute-mapper", "saml-user-property-mapper", "saml-role-list-mapper", "oidc-usermodel-property-mapper", "oidc-usermodel-attribute-mapper", "oidc-address-mapper" ] } }, { "id" : "3ab11d74-5e76-408a-b85a-26bf8950f979", @@ -1187,7 +1211,7 @@ "subType" : "anonymous", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "saml-role-list-mapper", "oidc-full-name-mapper", "oidc-usermodel-property-mapper", "oidc-address-mapper", "saml-user-property-mapper", "oidc-usermodel-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-user-attribute-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper", "saml-user-property-mapper", "saml-role-list-mapper", "oidc-address-mapper", "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "oidc-usermodel-attribute-mapper" ] } } ], "org.keycloak.keys.KeyProvider" : [ { @@ -1239,7 +1263,7 @@ "internationalizationEnabled" : false, "supportedLocales" : [ ], "authenticationFlows" : [ { - "id" : "9c0078f1-0f2e-47c0-8cd6-78851acb1504", + "id" : "be20e5ff-4ce9-48eb-b9e0-b948f04fbfe4", "alias" : "Account verification options", "description" : "Method with which to verity the existing account", "providerId" : "basic-flow", @@ -1261,7 +1285,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "ba90b374-8502-4a56-af42-f3a2aa931d32", + "id" : "029af9ca-193c-47cc-a6be-2361f9b08b69", "alias" : "Authentication Options", "description" : "Authentication options.", "providerId" : "basic-flow", @@ -1290,7 +1314,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "3bd266c5-b1fd-4f50-95f3-70e9e4e47fda", + "id" : "aaaf8559-3c0e-4e27-9a44-8322c9b14874", "alias" : "Browser - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -1312,7 +1336,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "11e6957d-ea18-4657-8639-fcf935eac694", + "id" : "ccee98c6-2523-481a-8a35-6465d644e97c", "alias" : "Direct Grant - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -1334,7 +1358,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "c5791aee-2c8c-4077-bc02-b57d6bdfa2c8", + "id" : "b5148d25-047f-48cb-8e11-643cc2899dc1", "alias" : "First broker login - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -1356,7 +1380,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "ffd20971-0d90-44d9-943c-a1aaef135be7", + "id" : "9f8ca1e9-8367-45b0-9ada-3da48daf16c9", "alias" : "Handle Existing Account", "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", "providerId" : "basic-flow", @@ -1378,7 +1402,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "9593a3cc-c7fd-40a4-95fb-05a7b06ad04c", + "id" : "395d349a-2e8e-454d-8caa-009e304f2b46", "alias" : "Reset - Conditional OTP", "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", "providerId" : "basic-flow", @@ -1400,7 +1424,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "7a81f226-53f3-407d-99cf-483b60eba0a9", + "id" : "22103f15-a3ae-46bb-8063-2b2308bade2a", "alias" : "User creation or linking", "description" : "Flow for the existing/non-existing user alternatives", "providerId" : "basic-flow", @@ -1423,7 +1447,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "90ab1710-d1e5-4f37-8c0e-5dc4acabf82b", + "id" : "5157fbeb-d121-434c-808f-06fcbf634880", "alias" : "Verify Existing Account by Re-authentication", "description" : "Reauthentication of existing account", "providerId" : "basic-flow", @@ -1445,7 +1469,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "ede4d86a-92d4-4803-bbb4-753f0fa7d33e", + "id" : "1c52aa5a-6d1b-4e47-b1b3-d747dc0bcf24", "alias" : "browser", "description" : "browser based authentication", "providerId" : "basic-flow", @@ -1481,7 +1505,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "aac32d41-adc7-4a06-948e-660dcb305218", + "id" : "135f84a3-208d-4764-b8df-68c64ada4ac7", "alias" : "clients", "description" : "Base authentication for clients", "providerId" : "client-flow", @@ -1517,7 +1541,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "ade1ad88-2db7-4986-9cfa-b948e0808655", + "id" : "5b647e08-40e0-45d7-9327-af31f7387abe", "alias" : "direct grant", "description" : "OpenID Connect Resource Owner Grant", "providerId" : "basic-flow", @@ -1546,7 +1570,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "570ea615-bd6b-49f2-abb3-e6bd27f44e0d", + "id" : "92438146-c5d5-4059-9e0e-f4532ce4ef63", "alias" : "docker auth", "description" : "Used by Docker clients to authenticate against the IDP", "providerId" : "basic-flow", @@ -1561,7 +1585,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "e878d8d8-0da7-4682-9124-483e8b8c6b59", + "id" : "c47ac645-5877-44b3-b12f-fbe0d01f8bc4", "alias" : "first broker login", "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", "providerId" : "basic-flow", @@ -1584,7 +1608,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "83aec897-602b-40c0-8df8-14abdba141bf", + "id" : "f60f7d34-9311-4d6d-ac84-05de99fd42e6", "alias" : "forms", "description" : "Username, password, otp and other auth forms.", "providerId" : "basic-flow", @@ -1606,7 +1630,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "ddb3f5d4-6395-4c09-ba1a-593dd3c85c16", + "id" : "ed2df37f-c19b-4356-9b7c-70c4e7e3f886", "alias" : "http challenge", "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes", "providerId" : "basic-flow", @@ -1628,7 +1652,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "3e707d1d-bfa5-4e54-a2e5-cccadad9e75b", + "id" : "1c515cf9-c6fa-44ba-be4c-3956e1a6bdda", "alias" : "registration", "description" : "registration flow", "providerId" : "basic-flow", @@ -1644,7 +1668,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "ed0157b5-6d81-47c9-8555-7082e92d5a62", + "id" : "644fd203-6727-4797-a62f-82accc38a990", "alias" : "registration form", "description" : "registration form", "providerId" : "form-flow", @@ -1680,7 +1704,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "c61c0070-feba-4770-b17f-7dfa3ba774ef", + "id" : "bf1690f9-daf5-4f9e-9ac2-51e5e3388f5a", "alias" : "reset credentials", "description" : "Reset credentials for a user if they forgot their password or something", "providerId" : "basic-flow", @@ -1716,7 +1740,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "4a928f9c-461e-437a-8eb8-3f120bddbb98", + "id" : "b2b85b81-f205-4136-988e-c0b984e45f14", "alias" : "saml ecp", "description" : "SAML ECP Profile Authentication Flow", "providerId" : "basic-flow", @@ -1732,13 +1756,13 @@ } ] } ], "authenticatorConfig" : [ { - "id" : "03d31f7c-8f00-4d23-b0fc-4413c20d64c9", + "id" : "208c6a70-7e63-45bf-99a4-d7867f61540d", "alias" : "create unique user config", "config" : { "require.password.update.after.registration" : "false" } }, { - "id" : "96db8eb0-e810-428e-a814-d753c8b086c6", + "id" : "6301fcc4-bfe0-49c1-888e-7b01ffcbec34", "alias" : "review profile config", "config" : { "update.profile.on.first.login" : "missing" diff --git a/fda-authentication-service/docker-entrypoint.sh b/fda-authentication-service/docker-entrypoint.sh new file mode 100644 index 0000000000000000000000000000000000000000..ca82a7badaf62a1fe8aef07e992df833d17755b6 --- /dev/null +++ b/fda-authentication-service/docker-entrypoint.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +/app/service-register.sh authentication-service 8080 +(while sleep 60; do bash /app/service-register.sh authentication-service 8080; done) & + +/opt/keycloak/bin/kc.sh start-dev --import-realm diff --git a/fda-authentication-service/service-register.sh b/fda-authentication-service/service-register.sh new file mode 100755 index 0000000000000000000000000000000000000000..72734426bbe787ca64d394dd514b79f4561cf2c6 --- /dev/null +++ b/fda-authentication-service/service-register.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +# $1 is used as the host name. + +EUREKA_HOST="discovery-service" +EUREKA_PORT="9090" +EUREKA_URI="http://$EUREKA_HOST:$EUREKA_PORT" + +SERVICE_NAME="$1" +SERVICE_PROTOCOL="http" +SERVICE_HOST="$1" +SERVICE_PORT="${2:-9000}" + +SERVICE_URI="$SERVICE_PROTOCOL://$SERVICE_HOST:$SERVICE_PORT" +HOME_URI="$SERVICE_URI/realms/dbrepo" +HEALTH_URI="$SERVICE_URI/health" + +# This is the URL shown in the "status" field in the +# instances section of the eureka dashboard. +# +# It's up to you to decide what the URL points to. Some +# information or status endpoint might be good. +STATUS_URI="$SERVICE_URI/health" + +# This is the name displayed to the right of the status +# on the eureka dashbard. If the app (FAKE_SERVICE) is +# registered with more than one hostname, they will be +# displayed as a comma-separated list. This hostname +# is part of the heartbeat message. +# +# If you'll have more than one host per service, +# make sure they have different host names. +HOST_NAME="${1:-fake01}" + +# Everyone of these parameters seem to be required. I don't know +# anything about secureVipAddress and vipAddress. +# +# dataCenterInfo must have a name of "MyOwn" or "Amazon". +# +# status can be UP, DOWN, STARTING, OUT_OF_SERVICE, UNKNOWN. +# if the registration status is STARTING, then the service +# will never be evicted. Also, simply sending a Heartbeat +# does not change the status. +# +# The metadata fields can be any information you want associated +# with a service. I recommend keeping it short. +# + +cat <<EOF > /tmp/json.json +{ + "instance": { + "instanceId": "$SERVICE_NAME:$SERVICE_NAME:$SERVICE_PORT", + "hostName": "$HOST_NAME", + "app": "$SERVICE_NAME", + "ipAddr": "$SERVICE_HOST", + "status": "UP", + "dataCenterInfo": { + "@class": "com.netflix.appinfo.MyDataCenterInfo", + "name": "MyOwn" + }, + "healthCheckUrl": "$HEALTH_URI", + "homePageUrl": "$HOME_URI", + "leaseInfo": { + "evictionDurationInSecs": 90 + }, + "metadata": { + "zone": "default", + "management.port": "8443" + }, + "port": { + "\$": "$SERVICE_PORT", + "@enabled": "true" + }, + "securePort": { + "\$": "$SERVICE_PORT", + "@enabled": "false" + }, + "vipAddress": "$SERVICE_HOST", + "secureVipAddress": "$SERVICE_HOST", + "statusPageUrl": "$STATUS_URI" + } +} +EOF + +curl --header "content-type: application/json" --data-binary @/tmp/json.json --silent $EUREKA_URI/eureka/apps/$SERVICE_NAME diff --git a/fda-ui/.env.example b/fda-ui/.env.example index 00f7fc8871f021c34ef36a11ea99a513cc027a58..9498c087a5670bd0e2a77ba52455abdac3e8b508 100644 --- a/fda-ui/.env.example +++ b/fda-ui/.env.example @@ -5,6 +5,6 @@ NODE_ENV=dev API="http://localhost:9095" BROKER_USERNAME=fda BROKER_PASSWORD=fda -SANDBOX=true +SANDBOX=false SHARED_FILESYSTEM=/tmp CLIENT_SECRET= diff --git a/fda-ui/layouts/default.vue b/fda-ui/layouts/default.vue index 73a9829d61d6489c0d0d4ff0792cb649150df8ba..9ba45c2b7351add71e11c935a0e89af64b1ae0d3 100644 --- a/fda-ui/layouts/default.vue +++ b/fda-ui/layouts/default.vue @@ -61,13 +61,6 @@ @click="login"> <v-icon left>mdi-login</v-icon> Login </v-btn> - <v-btn - v-if="!token" - class="mr-2" - color="primary" - to="/signup"> - <v-icon left>mdi-account-plus</v-icon> Signup - </v-btn> <v-btn v-if="user" to="/user" plain> {{ user.username }} <sup v-if="isDeveloper"> <v-tooltip bottom> diff --git a/fda-ui/nuxt.config.js b/fda-ui/nuxt.config.js index d537faae8af0e10bf79da71eb8386375309f5a6f..9327472ae752ff3efb05dd23cab75300030936f0 100644 --- a/fda-ui/nuxt.config.js +++ b/fda-ui/nuxt.config.js @@ -43,7 +43,7 @@ export default { plugins: [ { src: '@/plugins/toast', ssr: false }, { src: '@/plugins/vendors', ssr: false }, - { src: '@/plugins/axios' }, + { src: '@/plugins/axios', ssr: false }, { src: '@/plugins/vuex-persist.js', mode: 'client' } ], @@ -87,7 +87,6 @@ export default { }, proxy: { - '/auth': process.env.KEYCLOAK || 'https://localhost:8443', '/api': process.env.API || 'http://localhost:9095', '/pid': { target: process.env.API + '/api' || 'http://localhost:9095/api', diff --git a/fda-ui/package.json b/fda-ui/package.json index 7d4b5cb16b5aecfe883850a9afede905ec516847..e0e1c96189fd7d7bf0604df48d5fa0f11324266e 100644 --- a/fda-ui/package.json +++ b/fda-ui/package.json @@ -42,9 +42,11 @@ "node-fetch": "^2.6.1", "nuxt": "^2.15.8", "nuxt-i18n": "^6.15.4", + "qs": "^6.11.1", "sql-formatter": "^6.1.1", "vue": "^2.6.12", "vue-chartjs": "^4.1.1", + "vue-jwt-decode": "^0.1.0", "vue-toast-notification": "^0.5.4", "vue2-ace-editor": "^0.0.15", "vuetify": "^2.6.9", diff --git a/fda-ui/pages/login.vue b/fda-ui/pages/login.vue index 9457e6eb65394b2ed68aa824dcc9013df35e7561..5d61dd88f1a870cc102ca627c9261ffe32f84c72 100644 --- a/fda-ui/pages/login.vue +++ b/fda-ui/pages/login.vue @@ -56,6 +56,8 @@ </template> <script> +import VueJwtDecode from 'vue-jwt-decode' +import qs from 'qs' export default { data () { return { @@ -67,7 +69,8 @@ export default { username: null, password: null, grant_type: 'password', - client_secret: this.$config.client_secret + client_secret: this.$config.client_secret, + scope: 'openid' } } }, @@ -88,6 +91,11 @@ export default { return { headers: { Authorization: `Bearer ${this.token}` } } + }, + keycloakConfig () { + return { + headers: { ContentType: 'application/x-www-form-urlencoded' } + } } }, mounted () { @@ -106,11 +114,23 @@ export default { async login () { try { this.loading = true - const res = await this.$axios.post('/auth/realms/dbrepo/protocol/openid-connect/token', this.loginAccount) + const res = await this.$axios.post('/api/auth/realms/dbrepo/protocol/openid-connect/token', qs.stringify(this.loginAccount), this.keycloakConfig) console.debug('login user', res.data) - const { token } = res.data - this.$store.commit('SET_TOKEN', token) - await this.setUser() + // eslint-disable-next-line camelcase + const { access_token } = res.data + this.$store.commit('SET_TOKEN', access_token) + const data = VueJwtDecode.decode(access_token) + const user = { + id: data.sub, + firstname: data.given_name, + lastname: data.family_name, + username: data.preferred_username, + theme_dark: data.theme_dark, + email_verified: data.email_verified + } + this.$store.commit('SET_USER', user) + this.$vuetify.theme.dark = false + await this.$router.push({ path: this.$route.query.redirect ? this.$route.query.redirect : '/container' }) } catch (error) { const { status } = error.response if (status === 418) { @@ -126,19 +146,6 @@ export default { this.loading = false } }, - async setUser () { - try { - const res = await this.$axios.put('/api/auth', {}, this.config) - this.$store.commit('SET_USER', res.data) - this.$vuetify.theme.dark = res.data.theme_dark - await this.$router.push({ path: this.$route.query.redirect ? this.$route.query.redirect : '/container' }) - } catch (error) { - const { message } = error.response - console.error('Failed to load user information', error) - this.$toast.error('Failed to load user information: ' + message) - } - this.loading = false - }, signup () { this.$router.push('/signup') }, diff --git a/fda-ui/pages/signup.vue b/fda-ui/pages/signup.vue deleted file mode 100644 index 2b88602a4e7b006558a5f5759771a9c2dcbde622..0000000000000000000000000000000000000000 --- a/fda-ui/pages/signup.vue +++ /dev/null @@ -1,172 +0,0 @@ -<template> - <div> - <v-form ref="form" v-model="valid" @submit.prevent="submit"> - <v-card flat tile> - <v-card-title> - Create Account - </v-card-title> - <v-card-text> - <v-alert - v-if="mailVerify" - border="left" - color="info"> - Before you can use the repository, you will need to <i>confirm</i> your email address, make sure to check your spam folder. - </v-alert> - <v-row dense> - <v-col sm="6"> - <v-text-field - v-model="createAccount.email" - type="email" - autocomplete="off" - autofocus - required - name="email" - :rules="[v => !!v || $t('Required')]" - hint="e.g. max.mustermann@work.com" - label="Work E-Mail Address *" /> - </v-col> - </v-row> - <v-row dense> - <v-col sm="6"> - <v-text-field - v-model="createAccount.username" - autocomplete="off" - required - name="username" - :rules="[v => !!v || $t('Required'), - v => /^[a-z0-9]{3,}$/.test(v) || $t('Only lowercase letters, min. 3 length')]" - hint="e.g. mmustermann" - label="Username *" /> - </v-col> - </v-row> - <v-row dense> - <v-col sm="6"> - <v-text-field - v-model="createAccount.password" - autocomplete="off" - required - name="password" - :rules="[v => !!v || $t('Required')]" - type="password" - label="Password *" /> - </v-col> - </v-row> - <v-row dense> - <v-col sm="6"> - <v-text-field - v-model="password2" - autocomplete="off" - required - name="password-confirm" - :rules="[v => !!v || $t('Required'), v => (!!v && v) === createAccount.password || $t('Not matching!')]" - type="password" - label="Repeat Password *" /> - </v-col> - </v-row> - <v-row v-if="sandbox" dense> - <v-col sm="6"> - <v-checkbox - v-model="consent" - required - name="consent" - :rules="[v => !!v || $t('Required')]" - label="I understand the warning and do not use production data" /> - </v-col> - </v-row> - <v-row v-if="sandbox" dense> - <v-col sm="6"> - <v-checkbox - v-model="privacy" - required - name="privacy" - :rules="[v => !!v || $t('Required')]" - label="I have read and accept the privacy statement" /> - </v-col> - </v-row> - </v-card-text> - <v-card-text> - <v-btn - id="login" - :disabled="!valid" - color="primary" - type="submit" - name="submit" - :loading="loading" - @click="register"> - Submit - </v-btn> - </v-card-text> - </v-card> - </v-form> - </div> -</template> - -<script> -export default { - data () { - return { - loading: false, - error: false, // XXX: `error` is never changed - valid: false, - password2: null, - privacy: false, - consent: false, - createAccount: { - username: null, - email: null, - password: null - } - } - }, - computed: { - loadingColor () { - return this.error ? 'red lighten-2' : 'primary' - }, - sandbox () { - return this.$config.sandbox - }, - mailVerify () { - return this.$config.mailVerify - } - }, - methods: { - submit () { - this.$refs.form.validate() - }, - async register () { - const url = '/api/user' - try { - this.loading = true - const res = await this.$axios.post(url, this.createAccount) - console.debug('create user', res.data) - this.$toast.success('Success. Check your inbox!') - this.$router.push('/login') - } catch (err) { - if (err.response !== undefined && err.response.status !== undefined) { - if (err.response.status === 417) { - this.$toast.error('This e-mail address is taken') - console.error('email taken', err) - this.loading = false - return - } - if (err.response.status === 409) { - this.$toast.error('This username is taken') - console.error('username taken', err) - this.loading = false - return - } - if (err.response.status === 428) { - this.$toast.warning('Account was created but the server failed to send a mail') - console.warn('email sending failed', err) - this.loading = false - return - } - } - console.error('create user failed', err) - this.$toast.error('Failed to create user') - } - this.loading = false - } - } -} -</script> diff --git a/fda-ui/yarn.lock b/fda-ui/yarn.lock index 421217b621020514a9f20925a4ba668b5d2735c6..c13f79ce87b2b72bc9c6e59bcb8157db87582c41 100644 --- a/fda-ui/yarn.lock +++ b/fda-ui/yarn.lock @@ -2133,6 +2133,15 @@ postcss "^8.4.14" source-map "^0.6.1" +"@vue/compiler-sfc@2.7.14": + version "2.7.14" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-2.7.14.tgz#3446fd2fbb670d709277fc3ffa88efc5e10284fd" + integrity sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA== + dependencies: + "@babel/parser" "^7.18.4" + postcss "^8.4.14" + source-map "^0.6.1" + "@vue/component-compiler-utils@^2.3.1": version "2.6.0" resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-2.6.0.tgz#aa46d2a6f7647440b0b8932434d22f12371e543b" @@ -10205,6 +10214,13 @@ qs@6.10.3: dependencies: side-channel "^1.0.4" +qs@^6.11.1: + version "6.11.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.1.tgz#6c29dff97f0c0060765911ba65cbc9764186109f" + integrity sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ== + dependencies: + side-channel "^1.0.4" + qs@^6.9.4: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" @@ -12242,6 +12258,13 @@ vue-i18n@^8.25.0: resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-8.27.2.tgz#b649a65ff42b7d1a482679b732902f889965a068" integrity sha512-QVzn7u2WVH8F7eSKIM00lujC7x1mnuGPaTnDTmB01Hd709jDtB9kYtBqM+MWmp5AJRx3gnqAdZbee9MelqwFBg== +vue-jwt-decode@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/vue-jwt-decode/-/vue-jwt-decode-0.1.0.tgz#f9caf7b9030d5459cc567b1c3117d9d1f291458f" + integrity sha512-4iP0NzYHkAF7G13tYPc/nudk4oNpB8GCVZupc7lekxXok1XKEgefNaGTpDT14g7RKe5H9GaMphPduDj4UVfZwQ== + dependencies: + vue "^2.3.3" + vue-loader@^15.9.7: version "15.10.0" resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.10.0.tgz#2a12695c421a2a2cc2138f05a949d04ed086e38b" @@ -12317,6 +12340,14 @@ vue2-ace-editor@^0.0.15: dependencies: brace "^0.11.0" +vue@^2.3.3: + version "2.7.14" + resolved "https://registry.yarnpkg.com/vue/-/vue-2.7.14.tgz#3743dcd248fd3a34d421ae456b864a0246bafb17" + integrity sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ== + dependencies: + "@vue/compiler-sfc" "2.7.14" + csstype "^3.1.0" + vue@^2.6.12: version "2.7.10" resolved "https://registry.yarnpkg.com/vue/-/vue-2.7.10.tgz#ae516cc6c88e1c424754468844218fdd5e280f40"