Skip to content
Snippets Groups Projects
Unverified Commit 2657fc56 authored by Cam Saul's avatar Cam Saul Committed by GitHub
Browse files

Merge pull request #8079 from...

Merge pull request #8079 from metabase/respect-password-complexity-requirements-when-generating-new-password

Respect password complexity requirements when generating new password
parents a9cf679f b3fd16e3
No related branches found
No related tags found
No related merge requests found
...@@ -160,10 +160,7 @@ export default class PeopleListingApp extends Component { ...@@ -160,10 +160,7 @@ export default class PeopleListingApp extends Component {
}); });
} else { } else {
// generate a password // generate a password
const password = MetabaseUtils.generatePassword( const password = MetabaseUtils.generatePassword();
14,
MetabaseSettings.get("password_complexity"),
);
// trigger the reset // trigger the reset
this.props.resetPasswordManually(user, password); this.props.resetPasswordManually(user, password);
......
...@@ -93,7 +93,9 @@ export default class PasswordResetApp extends Component { ...@@ -93,7 +93,9 @@ export default class PasswordResetApp extends Component {
render() { render() {
const { resetError, resetSuccess, newUserJoining } = this.props; const { resetError, resetSuccess, newUserJoining } = this.props;
const passwordComplexity = MetabaseSettings.passwordComplexity(false); const passwordComplexity = MetabaseSettings.passwordComplexityDescription(
false,
);
const requestLink = ( const requestLink = (
<Link to="/auth/forgot_password" className="link"> <Link to="/auth/forgot_password" className="link">
......
...@@ -80,7 +80,11 @@ const MetabaseSettings = { ...@@ -80,7 +80,11 @@ const MetabaseSettings = {
); );
}, },
passwordComplexity: function(capitalize) { // returns a map that looks like {total: 6, digit: 1}
passwordComplexityRequirements: () => mb_settings.password_complexity,
// returns a description of password complexity requirements rather than the actual map of requirements
passwordComplexityDescription: function(capitalize) {
const complexity = this.get("password_complexity"); const complexity = this.get("password_complexity");
const clauseDescription = function(clause) { const clauseDescription = function(clause) {
......
import generatePassword from "password-generator"; import generatePassword from "password-generator";
import { t } from "c-3po"; import { t } from "c-3po";
import MetabaseSettings from "metabase/lib/settings";
const LAYOUT_PROPS = [ const LAYOUT_PROPS = [
"m", "m",
...@@ -42,20 +43,21 @@ function s4() { ...@@ -42,20 +43,21 @@ function s4() {
// provides functions for building urls to things we care about // provides functions for building urls to things we care about
let MetabaseUtils = { let MetabaseUtils = {
generatePassword: function(length, complexity) { // generate a password that satisfies `complexity` requirements, by default the ones that come back in the
const len = length || 14; // `password_complexity` Setting; must be a map like {total: 6, number: 1}
generatePassword: function(complexity) {
if (!complexity) { complexity =
return generatePassword(len, false); complexity || MetabaseSettings.passwordComplexityRequirements() || {};
} else { // fall back to length of 14 if the password_complexity Setting isn't set or total isn't passed in
let password = ""; const len = complexity.total || 14;
let tries = 0;
while (!isStrongEnough(password) && tries < 100) { let password = "";
password = generatePassword(len, false, /[\w\d\?\-]/); let tries = 0;
tries++; while (!isStrongEnough(password) && tries < 100) {
} password = generatePassword(len, false, /[\w\d\?\-]/);
return password; tries++;
} }
return password;
function isStrongEnough(password) { function isStrongEnough(password) {
let uc = password.match(/([A-Z])/g); let uc = password.match(/([A-Z])/g);
......
...@@ -149,7 +149,7 @@ export default class UserStep extends Component { ...@@ -149,7 +149,7 @@ export default class UserStep extends Component {
let { activeStep, setActiveStep, stepNumber, userDetails } = this.props; let { activeStep, setActiveStep, stepNumber, userDetails } = this.props;
let { formError, passwordError, valid } = this.state; let { formError, passwordError, valid } = this.state;
const passwordComplexityDesc = MetabaseSettings.passwordComplexity(); const passwordComplexityDesc = MetabaseSettings.passwordComplexityDescription();
const stepText = const stepText =
activeStep <= stepNumber activeStep <= stepNumber
? t`What should we call you?` ? t`What should we call you?`
......
...@@ -88,7 +88,9 @@ export default class SetUserPassword extends Component { ...@@ -88,7 +88,9 @@ export default class SetUserPassword extends Component {
render() { render() {
const { updatePasswordResult } = this.props; const { updatePasswordResult } = this.props;
let { formError, valid } = this.state; let { formError, valid } = this.state;
const passwordComplexity = MetabaseSettings.passwordComplexity(true); const passwordComplexity = MetabaseSettings.passwordComplexityDescription(
true,
);
formError = formError =
updatePasswordResult && !formError ? updatePasswordResult : formError; updatePasswordResult && !formError ? updatePasswordResult : formError;
......
import MetabaseUtils from "metabase/lib/utils"; import MetabaseUtils from "metabase/lib/utils";
import MetabaseSettings from "metabase/lib/settings";
describe("utils", () => { describe("utils", () => {
describe("generatePassword", () => { describe("generatePassword", () => {
it("defaults to length 14 passwords", () => { it("defaults to complexity requirements from Settings", () => {
expect(MetabaseUtils.generatePassword().length).toBe(14); MetabaseSettings.set("password_complexity", { total: 10 });
expect(MetabaseUtils.generatePassword().length).toBe(10);
});
it("falls back to length 14 passwords", () => {
expect(MetabaseUtils.generatePassword({}).length).toBe(14);
}); });
it("creates passwords for the length we specify", () => { it("creates passwords for the length we specify", () => {
expect(MetabaseUtils.generatePassword(25).length).toBe(25); expect(MetabaseUtils.generatePassword({ total: 25 }).length).toBe(25);
}); });
it("can enforce ", () => { it("can enforce ", () => {
expect( expect(
MetabaseUtils.generatePassword(14, { digit: 2 }).match(/([\d])/g) MetabaseUtils.generatePassword({ total: 14, digit: 2 }).match(/([\d])/g)
.length >= 2, .length >= 2,
).toBe(true); ).toBe(true);
}); });
it("can enforce digit requirements", () => { it("can enforce digit requirements", () => {
expect( expect(
MetabaseUtils.generatePassword(14, { digit: 2 }).match(/([\d])/g) MetabaseUtils.generatePassword({ total: 14, digit: 2 }).match(/([\d])/g)
.length >= 2, .length >= 2,
).toBe(true); ).toBe(true);
}); });
it("can enforce uppercase requirements", () => { it("can enforce uppercase requirements", () => {
expect( expect(
MetabaseUtils.generatePassword(14, { uppercase: 2 }).match(/([A-Z])/g) MetabaseUtils.generatePassword({ total: 14, uppercase: 2 }).match(
.length >= 2, /([A-Z])/g,
).length >= 2,
).toBe(true); ).toBe(true);
}); });
it("can enforce special character requirements", () => { it("can enforce special character requirements", () => {
expect( expect(
MetabaseUtils.generatePassword(14, { special: 2 }).match( MetabaseUtils.generatePassword({ total: 14, special: 2 }).match(
/([!@#\$%\^\&*\)\(+=._-{}])/g, /([!@#\$%\^\&*\)\(+=._-{}])/g,
).length >= 2, ).length >= 2,
).toBe(true); ).toBe(true);
......
(ns metabase.util.password (ns metabase.util.password
"Utility functions for checking passwords against hashes and for making sure passwords match complexity requirements."
(:require [cemerick.friend.credentials :as creds] (:require [cemerick.friend.credentials :as creds]
[metabase [metabase
[config :as config] [config :as config]
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment