mirror of https://github.com/portainer/portainer
feat(password) EE-2690 enforce strong password policy (#6751)
* feat(password) EE-2690 enforce strong password policy * feat(password) EE-2690 disable create user button if password is not valid * feat(password) EE-2690 show force password change warning only when week password is detected * feat(password) EE-2690 prevent users leave account page by clicking add access token button Co-authored-by: Simon Meng <simon.meng@portainer.io>pull/6662/head^2
parent
9ebc963082
commit
85ad4e334a
@ -0,0 +1,33 @@
|
||||
package passwordutils
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
const MinPasswordLen = 12
|
||||
|
||||
func lengthCheck(password string) bool {
|
||||
return len(password) >= MinPasswordLen
|
||||
}
|
||||
|
||||
func comboCheck(password string) bool {
|
||||
count := 0
|
||||
regexps := [4]*regexp.Regexp{
|
||||
regexp.MustCompile(`[a-z]`),
|
||||
regexp.MustCompile(`[A-Z]`),
|
||||
regexp.MustCompile(`[0-9]`),
|
||||
regexp.MustCompile(`[\W_]`),
|
||||
}
|
||||
|
||||
for _, re := range regexps {
|
||||
if re.FindString(password) != "" {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
|
||||
return count >= 3
|
||||
}
|
||||
|
||||
func StrengthCheck(password string) bool {
|
||||
return lengthCheck(password) && comboCheck(password)
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package passwordutils
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestStrengthCheck(t *testing.T) {
|
||||
type args struct {
|
||||
password string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantStrong bool
|
||||
}{
|
||||
{"Empty password", args{""}, false},
|
||||
{"Short password", args{"portainer"}, false},
|
||||
{"Short password", args{"portaienr!@#"}, false},
|
||||
{"Week password", args{"12345678!@#"}, false},
|
||||
{"Week password", args{"portaienr123"}, false},
|
||||
{"Good password", args{"Portainer123"}, true},
|
||||
{"Good password", args{"Portainer___"}, true},
|
||||
{"Good password", args{"^portainer12"}, true},
|
||||
{"Good password", args{"12%PORTAINER"}, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if gotStrong := StrengthCheck(tt.args.password); gotStrong != tt.wantStrong {
|
||||
t.Errorf("StrengthCheck() = %v, want %v", gotStrong, tt.wantStrong)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
import { react2angular } from '@/react-tools/react2angular';
|
||||
|
||||
import { MinPasswordLen } from '../helpers/password';
|
||||
|
||||
function PasswordCombination() {
|
||||
return (
|
||||
<ul className="text-muted">
|
||||
<li className="ml-8"> Special characters </li>
|
||||
<li className="ml-8"> Lower case characters </li>
|
||||
<li className="ml-8"> Upper case characters </li>
|
||||
<li className="ml-8"> Numeric characters </li>
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
export function ForcePasswordUpdateHint() {
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
<i
|
||||
className="fa fa-exclamation-triangle orange-icon"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<b> Please update your password to continue </b>
|
||||
</p>
|
||||
|
||||
<p className="text-muted">
|
||||
To ensure the security of your account, please update your password to a
|
||||
stronger password using a combination of at least 3 of the following:
|
||||
</p>
|
||||
|
||||
<PasswordCombination />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function PasswordCheckHint() {
|
||||
return (
|
||||
<div>
|
||||
<p className="text-muted">
|
||||
<i className="fa fa-times red-icon space-right" aria-hidden="true">
|
||||
{' '}
|
||||
</i>
|
||||
<span>
|
||||
The password must be at least {MinPasswordLen} characters long,
|
||||
including a combination of one character of three of the below:
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<PasswordCombination />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const ForcePasswordUpdateHintAngular = react2angular(
|
||||
ForcePasswordUpdateHint,
|
||||
[]
|
||||
);
|
||||
export const PasswordCheckHintAngular = react2angular(PasswordCheckHint, []);
|
@ -0,0 +1,22 @@
|
||||
export const MinPasswordLen = 12;
|
||||
|
||||
function lengthCheck(password: string) {
|
||||
return password.length >= MinPasswordLen;
|
||||
}
|
||||
|
||||
function comboCheck(password: string) {
|
||||
let count = 0;
|
||||
const regexps = [/[a-z]/, /[A-Z]/, /[0-9]/, /[\W_]/];
|
||||
|
||||
regexps.forEach((re) => {
|
||||
if (password.match(re) != null) {
|
||||
count += 1;
|
||||
}
|
||||
});
|
||||
|
||||
return count >= 3;
|
||||
}
|
||||
|
||||
export function StrengthCheck(password: string) {
|
||||
return lengthCheck(password) && comboCheck(password);
|
||||
}
|
Loading…
Reference in new issue