Update auth-setup page to handle resetting user password

rancher/dashboard#3143
This commit is contained in:
Westly Wright 2021-06-25 09:51:22 -07:00
parent 92c292aa71
commit 999a0bff25
No known key found for this signature in database
GPG Key ID: 4FAB3D8673DC54A3
6 changed files with 129 additions and 47 deletions

View File

@ -2548,7 +2548,7 @@ probe:
placeholder: Select a check type
project:
members:
members:
label: Members
user: User
role: Role
@ -3098,26 +3098,24 @@ servicesPage:
label: Service Type
setup:
welcome: Welcome to {vendor}!
setPassword: The first order of business is to set a strong password for the default <code>admin</code> user. We suggest using this random one generated just for you, but enter your own if you like.
newPassword: New Password
confirmPassword: Confirm New Password
useRandom: Use a randomly generated password
useManual: Set a specific password to use
defaultPasswordError: It looks like this is your first time visting the Rancher UI, but the local admin account password is already set to something unique. Log in with that account below to continue the setup process.
telemetry:
label: Allow collection of anonymous statistics to help us improve Rancher
tip: 'Rancher Labs would like to collect a bit of anonymized information
about the configuration of your installation to help make Rio better.
Your data will not be shared with anyone else, and no information about
what specific resources or endpoints you are deploying is included.
Once enabled you can view exactly what data will be sent at <code>/v1-telemetry</code>.
<a href="https://rancher.com/docs/rancher/v2.x/en/faq/telemetry/" target="_blank" rel="noopener noreferrer nofollow">More Info</a>'
eula: I agree to the <a href="https://rancher.com/eula" target="_blank" rel="noopener noreferrer nofollow">terms and conditions</a> for using Rancher.
newPassword: New Password
newUserSetPassword: The first order of business is to set a strong password for <code>{username}</code>. We suggest using this random one generated just for you, but enter your own if you like.
serverUrl:
label: Server URL
tip: What URL should be used for this Rancher installation? All the nodes in your clusters will need to be able to reach this. You can skip setting this for now, and update it later in General Settings>Advanced Settings.
skip: Skip
tip: What URL should be used for this Rancher installation? All the nodes in your clusters will need to be able to reach this. You can skip setting this for now, and update it later in General Settings>Advanced Settings.
setPassword: The first order of business is to set a strong password for the default <code>{username}</code> user. We suggest using this random one generated just for you, but enter your own if you like.
telemetry:
label: Allow collection of anonymous statistics to help us improve Rancher
tip: >-
Rancher Labs would like to collect a bit of anonymized information about the configuration of your installation to help make Rio better. Your data will not be shared with anyone else, and no information about what specific resources or endpoints you are deploying is included. Once enabled you can view exactly what data will be sent at <code>/v1-telemetry</code>. <a href="https://rancher.com/docs/rancher/v2.x/en/faq/telemetry/" target="_blank" rel="noopener noreferrer nofollow">More
Info</a>
useManual: Set a specific password to use
useRandom: Use a randomly generated password
welcome: Welcome to {vendor}!
sortableTable:
actionAvailability:

View File

@ -13,6 +13,7 @@ export const GITHUB_CODE = 'code';
export const GITHUB_NONCE = 'state';
export const GITHUB_SCOPE = 'scope';
export const GITHUB_REDIRECT = 'redirect_uri';
export const USERNAME = 'un';
// General
export const _FLAGGED = null; // The value for a key-only flag, like `?desc`

View File

@ -7,6 +7,7 @@ import { applyProducts } from '@/store/type-map';
import { findBy } from '@/utils/array';
import { ClusterNotFoundError } from '@/utils/error';
import { get } from '@/utils/object';
import { isEmpty } from 'lodash';
let beforeEachSetup = false;
@ -93,10 +94,10 @@ export default async function({
if (ok) {
if (initialPass) {
return redirect({ name: 'auth-setup', query: { [SETUP]: initialPass } });
} else {
return redirect({ name: 'auth-setup' });
store.dispatch('auth/setInitialPass', initialPass);
}
return redirect({ name: 'auth-setup' });
} else {
const t = store.getters['i18n/t'];
@ -125,6 +126,14 @@ export default async function({
}
if ( store.getters['auth/enabled'] !== false && !store.getters['auth/loggedIn'] ) {
const v3User = await findV3User(store);
if (v3User?.mustChangePassword) {
store.commit('auth/gotUser', v3User);
return redirect({ name: 'auth-setup' });
}
// In newer versions the API calls return the auth state instead of having to make a new call all the time.
const fromHeader = store.getters['auth/fromHeader'];
@ -228,6 +237,15 @@ async function findMe(store) {
return me;
}
async function findV3User(store) {
const user = await store.dispatch('rancher/findAll', {
type: NORMAN.USER,
opt: { url: '/v3/users?me=true' }
});
return isEmpty(user[0]) ? {} : user[0];
}
async function tryInitialSetup(store, password = 'admin') {
try {
const res = await store.dispatch('auth/login', {

View File

@ -11,9 +11,10 @@ import { configType } from '@/models/management.cattle.io.authconfig';
import { mapGetters } from 'vuex';
import { importLogin } from '@/utils/dynamic-importer';
import { _ALL_IF_AUTHED } from '@/plugins/steve/actions';
import { MANAGEMENT } from '@/config/types';
import { MANAGEMENT, NORMAN } from '@/config/types';
import { SETTING } from '@/config/settings';
import { LOGIN_ERRORS } from '@/store/auth';
import isEmpty from 'lodash/isEmpty';
import { getVendor, getProduct, setVendor } from '../../config/private-label';
export default {
@ -190,6 +191,18 @@ export default {
password: this.password
}
});
const user = await this.$store.dispatch('rancher/findAll', {
type: NORMAN.USER,
opt: { url: '/v3/users?me=true' }
});
if (!isEmpty(user) && !isEmpty(user[0])) {
this.$store.dispatch('auth/gotUser', user[0]);
this.needsSetup = user[0]?.mustChangePassword ?? false;
}
if ( this.remember ) {
this.$cookies.set(USERNAME, this.username, {
encode: x => x,
@ -202,7 +215,8 @@ export default {
}
if (this.needsSetup) {
this.$router.push({ name: 'auth-setup', query: { setup: this.password } });
this.$store.dispatch('auth/setInitialPass', this.password);
this.$router.push({ name: 'auth-setup' });
} else {
this.$router.replace('/');
}

View File

@ -28,8 +28,9 @@ export default {
}
const firstLoginSetting = store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.FIRST_LOGIN);
const v3User = store.getters['auth/v3User'] ?? {};
if (firstLoginSetting?.value !== 'true') {
if (firstLoginSetting?.value !== 'true' && !v3User?.mustChangePassword) {
return redirect('/');
}
},
@ -51,8 +52,10 @@ export default {
}
const principals = await store.dispatch('rancher/findAll', { type: NORMAN.PRINCIPAL, opt: { url: '/v3/principals' } });
const me = findBy(principals, 'me', true);
const current = route.query[SETUP] || 'admin';
const current = route.query[SETUP] || store.getters['auth/initialPass'] || 'admin';
const v3User = store.getters['auth/v3User'] ?? {};
let serverUrl;
@ -69,12 +72,15 @@ export default {
product: getProduct(),
step: parseInt(route.query.step, 10) || 1,
useRandom: false,
haveCurrent: !!current,
username: 'admin',
useRandom: false,
haveCurrent: !!current,
username: me?.loginName ?? 'admin',
mustChangePassword: v3User?.mustChangePassword ?? false,
current,
password: '',
confirm: '',
password: '',
confirm: '',
v3User,
serverUrl,
@ -89,7 +95,7 @@ export default {
computed: {
passwordSubmitDisabled() {
if (!this.eula) {
if (!this.eula && !this.mustChangePassword) {
return true;
}
@ -138,13 +144,15 @@ export default {
methods: {
async finishPassword(buttonCb) {
try {
await this.$store.dispatch('loadManagement');
if (!this.mustChangePassword) {
await this.$store.dispatch('loadManagement');
await Promise.all([
setSetting(this.$store, SETTING.EULA_AGREED, (new Date()).toISOString() ),
setSetting(this.$store, SETTING.TELEMETRY, this.telemetry ? 'in' : 'out'),
setSetting(this.$store, SETTING.FIRST_LOGIN, 'false'),
]);
await Promise.all([
setSetting(this.$store, SETTING.EULA_AGREED, (new Date()).toISOString() ),
setSetting(this.$store, SETTING.TELEMETRY, this.telemetry ? 'in' : 'out'),
setSetting(this.$store, SETTING.FIRST_LOGIN, 'false'),
]);
}
await this.$store.dispatch('rancher/request', {
url: '/v3/users?action=changepassword',
@ -154,8 +162,20 @@ export default {
newPassword: this.password
},
});
this.step = 2;
buttonCb(true);
if (this.mustChangePassword) {
const user = this.v3User;
user.mustChangePassword = false;
this.$store.dispatch('auth/gotUser', user);
buttonCb(true);
this.done();
} else {
this.step = 2;
buttonCb(true);
}
} catch (err) {
buttonCb(false);
}
@ -186,9 +206,10 @@ export default {
</h1>
<template v-if="step===1">
<p class="text-center mb-40 mt-20 setup-title">
<t k="setup.setPassword" :raw="true" />
</p>
<p
class="text-center mb-40 mt-20 setup-title"
v-html="t('setup.setPassword', { username }, true)"
></p>
<!-- For password managers... -->
<input type="hidden" name="username" autocomplete="username" :value="username" />
@ -218,15 +239,17 @@ export default {
label-key="setup.confirmPassword"
/>
<hr class="mt-40 mb-40 " />
<div v-if="!mustChangePassword">
<hr class="mt-40 mb-40 " />
<div class="checkbox">
<Checkbox v-model="telemetry" :label="t('setup.telemetry.label')" type="checkbox" />
<i v-tooltip="{content:t('setup.telemetry.tip', {}, true), delay: {hide:500}, autoHide: false}" class="icon icon-info" />
</div>
<div class="checkbox pt-10 eula">
<Checkbox v-model="eula" type="checkbox" />
<span v-html="t('setup.eula', {}, true)"></span>
<div class="checkbox">
<Checkbox v-model="telemetry" :label="t('setup.telemetry.label')" type="checkbox" />
<i v-tooltip="{content:t('setup.telemetry.tip', {}, true), delay: {hide:500}, autoHide: false}" class="icon icon-info" />
</div>
<div class="checkbox pt-10 eula">
<Checkbox v-model="eula" type="checkbox" />
<span v-html="t('setup.eula', {}, true)"></span>
</div>
</div>
<div class="text-center mt-20">

View File

@ -28,6 +28,8 @@ export const state = function() {
hasAuth: null,
loggedIn: false,
principalId: null,
v3User: null,
initialPass: null,
};
};
@ -48,6 +50,14 @@ export const getters = {
return state.principalId;
},
v3User(state) {
return state.v3User;
},
initialPass(state) {
return state.initialPass;
},
isGithub(state) {
return state.principalId && state.principalId.startsWith('github_user://');
}
@ -58,6 +68,10 @@ export const mutations = {
state.fromHeader = fromHeader;
},
gotUser(state, v3User) {
state.v3User = v3User;
},
hasAuth(state, hasAuth) {
state.hasAuth = !!hasAuth;
},
@ -75,7 +89,13 @@ export const mutations = {
state.loggedIn = false;
state.principalId = null;
state.v3User = null;
state.initialPass = null;
},
initialPass(state, pass) {
state.initialPass = pass;
}
};
export const actions = {
@ -83,6 +103,14 @@ export const actions = {
commit('gotHeader', fromHeader);
},
gotUser({ commit }, user) {
commit('gotUser', user);
},
setInitialPass({ commit }, pass) {
commit('initialPass', pass);
},
getAuthProviders({ dispatch }) {
return dispatch('rancher/findAll', {
type: 'authProvider',