-
-
Notifications
You must be signed in to change notification settings - Fork 46
#2849 - Generate random password for users #2866
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 6 commits
cf4c8de
a757a8b
b4754ba
f49da1d
65df525
7a98167
ec8e31b
197b783
3a35173
a96c8b6
ff4a644
d9d765f
4a692d1
b7092b1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,29 @@ | ||
<template lang='pug'> | ||
label.field | ||
component.field(:is='mode === "manual" ? "label" : "div"') | ||
.label(v-if='label') {{ label }} | ||
|
||
.inputgroup.c-mode-auto( | ||
v-if='mode === "auto"' | ||
v-error:[name]='' | ||
) | ||
.input.width-with-single-addon.has-ellipsis.c-auto-password( | ||
:data-test='name' | ||
) {{ ephemeral.randomPassword }} | ||
|
||
.addons | ||
button.is-success.c-copy-btn( | ||
type='button' | ||
@click.stop='copyPassword' | ||
) | ||
i18n Copy | ||
|
||
i18n.c-feedback( | ||
v-if='ephemeral.showCopyFeedback' | ||
) Copied to clipboard! | ||
|
||
.inputgroup( | ||
v-error:[name]='{ attrs: { "data-test": "badPassword" }}' | ||
v-else | ||
v-error:[name]='' | ||
) | ||
input.input.with-single-addon( | ||
:type='isLock ? "password" : "text"' | ||
|
@@ -17,7 +38,6 @@ label.field | |
.addons | ||
button.is-icon( | ||
type='button' | ||
v-if='hasIconRight' | ||
:aria-label='L("Toggle password visibility")' | ||
:aria-pressed='!isLock' | ||
@click.prevent='isLock = !isLock' | ||
|
@@ -26,13 +46,22 @@ label.field | |
</template> | ||
|
||
<script> | ||
import Tooltip from '@components/Tooltip.vue' | ||
import validationsDebouncedMixins from '@view-utils/validationsDebouncedMixins.js' | ||
import { L } from '@common/common.js' | ||
|
||
export default ({ | ||
name: 'PasswordForm', | ||
components: { | ||
Tooltip | ||
}, | ||
data () { | ||
return { | ||
isLock: true | ||
isLock: true, | ||
ephemeral: { | ||
randomPassword: '', | ||
showCopyFeedback: false | ||
} | ||
} | ||
}, | ||
mixins: [validationsDebouncedMixins], | ||
|
@@ -42,6 +71,11 @@ export default ({ | |
required: false, | ||
default: 'password' | ||
}, | ||
mode: { | ||
type: String, | ||
requried: false, | ||
default: 'manual' // 'manual' | 'auto' | ||
}, | ||
label: { | ||
type: String, | ||
required: false | ||
|
@@ -50,10 +84,6 @@ export default ({ | |
type: Object, | ||
required: true | ||
}, | ||
hasIconRight: { | ||
type: Boolean, | ||
default: true | ||
}, | ||
showPlaceholder: { | ||
type: Boolean, | ||
default: false | ||
|
@@ -67,15 +97,95 @@ export default ({ | |
required: false | ||
} | ||
}, | ||
methods: { | ||
generateRandomPassword (pwLen = 32) { | ||
let genPassword = '' | ||
|
||
if (window?.Cypress) { | ||
|
||
// For easier debugging, use the common default password in Cypress test. | ||
genPassword = '123456789' | ||
} else { | ||
const bytes = new Uint8Array(Math.ceil(pwLen / 2)) | ||
crypto.getRandomValues(bytes) | ||
|
||
genPassword = Array.from(bytes).map(b => b.toString(36) // [0-9a-z] => 36 characters | ||
corrideat marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
.padStart(2, '0')).join('') | ||
|
||
if (pwLen % 2 === 1) { | ||
genPassword = genPassword.slice(1) | ||
} | ||
} | ||
|
||
this.ephemeral.randomPassword = genPassword | ||
this.$v.form[this.name].$model = genPassword | ||
}, | ||
copyPassword () { | ||
const pw = this.ephemeral.randomPassword | ||
const copyToClipBoard = () => { | ||
navigator.clipboard.writeText(pw) | ||
this.ephemeral.showCopyFeedback = true | ||
|
||
setTimeout(() => { | ||
this.ephemeral.showCopyFeedback = false | ||
}, 1500) | ||
} | ||
|
||
if (navigator.share) { | ||
navigator.share({ | ||
title: L('Your password'), | ||
text: pw | ||
}).catch((error) => { | ||
console.error('navigator.share failed with:', error) | ||
copyToClipBoard() | ||
}) | ||
} else { | ||
copyToClipBoard() | ||
} | ||
} | ||
}, | ||
created () { | ||
this.isLock = !this.showPassword | ||
if (this.mode === 'auto') { | ||
this.generateRandomPassword() | ||
} else { | ||
this.isLock = !this.showPassword | ||
} | ||
} | ||
}: Object) | ||
</script> | ||
|
||
<style lang="scss" scoped> | ||
@import "@assets/style/_variables.scss"; | ||
|
||
.c-mode-auto { | ||
.c-auto-password { | ||
display: block; | ||
line-height: 2.75rem; | ||
padding-right: 5.5rem; | ||
} | ||
|
||
.addons { | ||
align-items: center; | ||
right: 0.5rem; | ||
} | ||
} | ||
|
||
.icon { | ||
cursor: pointer; | ||
pointer-events: initial !important; | ||
} | ||
|
||
button.c-copy-btn { | ||
min-height: unset; | ||
height: 1.75rem; | ||
border-radius: 3px; | ||
padding-left: 1rem; | ||
padding-right: 1rem; | ||
} | ||
|
||
.c-feedback { | ||
@include tooltip-style-common; | ||
top: calc(100% + 0.5rem); | ||
left: 50%; | ||
transform: translateX(-50%); | ||
} | ||
</style> |
Uh oh!
There was an error while loading. Please reload this page.