forked from geolba/tethys.backend
- add password strength meter for creating or editing user passwords
- add public opensearch api host
This commit is contained in:
parent
f4854d70b9
commit
010bead723
|
@ -14,7 +14,7 @@ import env from '#start/env';
|
||||||
// import { default as Dataset } from '#models/dataset';
|
// import { default as Dataset } from '#models/dataset';
|
||||||
|
|
||||||
const opensearchNode = env.get('OPENSEARCH_HOST', 'localhost');
|
const opensearchNode = env.get('OPENSEARCH_HOST', 'localhost');
|
||||||
const client = new Client({ node: `http://${opensearchNode}` }); // replace with your OpenSearch endpoint
|
const client = new Client({ node: `${opensearchNode}` }); // replace with your OpenSearch endpoint
|
||||||
|
|
||||||
export default class IndexDatasets extends BaseCommand {
|
export default class IndexDatasets extends BaseCommand {
|
||||||
static commandName = 'index:datasets';
|
static commandName = 'index:datasets';
|
||||||
|
|
|
@ -13,12 +13,13 @@
|
||||||
"assets/resources_js_Pages_Admin_Role_Show_vue.js": "http://localhost:8080/assets/resources_js_Pages_Admin_Role_Show_vue.js",
|
"assets/resources_js_Pages_Admin_Role_Show_vue.js": "http://localhost:8080/assets/resources_js_Pages_Admin_Role_Show_vue.js",
|
||||||
"assets/resources_js_Pages_Admin_Settings_vue.js": "http://localhost:8080/assets/resources_js_Pages_Admin_Settings_vue.js",
|
"assets/resources_js_Pages_Admin_Settings_vue.js": "http://localhost:8080/assets/resources_js_Pages_Admin_Settings_vue.js",
|
||||||
"assets/resources_js_Pages_Admin_User_Create_vue.js": "http://localhost:8080/assets/resources_js_Pages_Admin_User_Create_vue.js",
|
"assets/resources_js_Pages_Admin_User_Create_vue.js": "http://localhost:8080/assets/resources_js_Pages_Admin_User_Create_vue.js",
|
||||||
"assets/resources_js_Pages_Admin_User_Edit_vue.js": "http://localhost:8080/assets/resources_js_Pages_Admin_User_Edit_vue.js",
|
"assets/resources_js_Pages_Admin_User_Edit_vue-resources_js_Components_SimplePasswordMeter_password-m-6dc207.css": "http://localhost:8080/assets/resources_js_Pages_Admin_User_Edit_vue-resources_js_Components_SimplePasswordMeter_password-m-6dc207.css",
|
||||||
|
"assets/resources_js_Pages_Admin_User_Edit_vue-resources_js_Components_SimplePasswordMeter_password-m-6dc207.js": "http://localhost:8080/assets/resources_js_Pages_Admin_User_Edit_vue-resources_js_Components_SimplePasswordMeter_password-m-6dc207.js",
|
||||||
"assets/resources_js_Pages_Admin_User_Index_vue.js": "http://localhost:8080/assets/resources_js_Pages_Admin_User_Index_vue.js",
|
"assets/resources_js_Pages_Admin_User_Index_vue.js": "http://localhost:8080/assets/resources_js_Pages_Admin_User_Index_vue.js",
|
||||||
"assets/resources_js_Pages_Admin_User_Show_vue.js": "http://localhost:8080/assets/resources_js_Pages_Admin_User_Show_vue.js",
|
"assets/resources_js_Pages_Admin_User_Show_vue.js": "http://localhost:8080/assets/resources_js_Pages_Admin_User_Show_vue.js",
|
||||||
"assets/resources_js_Pages_App_vue.js": "http://localhost:8080/assets/resources_js_Pages_App_vue.js",
|
"assets/resources_js_Pages_App_vue.js": "http://localhost:8080/assets/resources_js_Pages_App_vue.js",
|
||||||
"assets/resources_js_Pages_Auth_AccountInfo_vue-resources_js_utils_toast_css.css": "http://localhost:8080/assets/resources_js_Pages_Auth_AccountInfo_vue-resources_js_utils_toast_css.css",
|
"assets/resources_js_Pages_Auth_AccountInfo_vue-resources_js_utils_toast_css-resources_js_Components_-06c7b5.css": "http://localhost:8080/assets/resources_js_Pages_Auth_AccountInfo_vue-resources_js_utils_toast_css-resources_js_Components_-06c7b5.css",
|
||||||
"assets/resources_js_Pages_Auth_AccountInfo_vue-resources_js_utils_toast_css.js": "http://localhost:8080/assets/resources_js_Pages_Auth_AccountInfo_vue-resources_js_utils_toast_css.js",
|
"assets/resources_js_Pages_Auth_AccountInfo_vue-resources_js_utils_toast_css-resources_js_Components_-06c7b5.js": "http://localhost:8080/assets/resources_js_Pages_Auth_AccountInfo_vue-resources_js_utils_toast_css-resources_js_Components_-06c7b5.js",
|
||||||
"assets/resources_js_Pages_Auth_Login_vue.js": "http://localhost:8080/assets/resources_js_Pages_Auth_Login_vue.js",
|
"assets/resources_js_Pages_Auth_Login_vue.js": "http://localhost:8080/assets/resources_js_Pages_Auth_Login_vue.js",
|
||||||
"assets/resources_js_Pages_Auth_Register_vue.js": "http://localhost:8080/assets/resources_js_Pages_Auth_Register_vue.js",
|
"assets/resources_js_Pages_Auth_Register_vue.js": "http://localhost:8080/assets/resources_js_Pages_Auth_Register_vue.js",
|
||||||
"assets/resources_js_Pages_Dashboard_vue.js": "http://localhost:8080/assets/resources_js_Pages_Dashboard_vue.js",
|
"assets/resources_js_Pages_Dashboard_vue.js": "http://localhost:8080/assets/resources_js_Pages_Dashboard_vue.js",
|
||||||
|
@ -49,9 +50,9 @@
|
||||||
"assets/vendors-node_modules_mdi_js_mdi_js-node_modules_vue-loader_dist_exportHelper_js.js": "http://localhost:8080/assets/vendors-node_modules_mdi_js_mdi_js-node_modules_vue-loader_dist_exportHelper_js.js",
|
"assets/vendors-node_modules_mdi_js_mdi_js-node_modules_vue-loader_dist_exportHelper_js.js": "http://localhost:8080/assets/vendors-node_modules_mdi_js_mdi_js-node_modules_vue-loader_dist_exportHelper_js.js",
|
||||||
"assets/vendors-node_modules_focus-trap_dist_focus-trap_esm_js-node_modules_notiwind_dist_index_esm_js.js": "http://localhost:8080/assets/vendors-node_modules_focus-trap_dist_focus-trap_esm_js-node_modules_notiwind_dist_index_esm_js.js",
|
"assets/vendors-node_modules_focus-trap_dist_focus-trap_esm_js-node_modules_notiwind_dist_index_esm_js.js": "http://localhost:8080/assets/vendors-node_modules_focus-trap_dist_focus-trap_esm_js-node_modules_notiwind_dist_index_esm_js.js",
|
||||||
"assets/vendors-node_modules_vue-facing-decorator_dist_esm_utils_js.js": "http://localhost:8080/assets/vendors-node_modules_vue-facing-decorator_dist_esm_utils_js.js",
|
"assets/vendors-node_modules_vue-facing-decorator_dist_esm_utils_js.js": "http://localhost:8080/assets/vendors-node_modules_vue-facing-decorator_dist_esm_utils_js.js",
|
||||||
"assets/vendors-node_modules_leaflet_dist_leaflet-src_js-node_modules_leaflet_src_control_Control_Att-4ee9a6.js": "http://localhost:8080/assets/vendors-node_modules_leaflet_dist_leaflet-src_js-node_modules_leaflet_src_control_Control_Att-4ee9a6.js",
|
"assets/vendors-node_modules_leaflet_dist_leaflet-src_js-node_modules_leaflet_src_control_Control_Att-adabdc.js": "http://localhost:8080/assets/vendors-node_modules_leaflet_dist_leaflet-src_js-node_modules_leaflet_src_control_Control_Att-adabdc.js",
|
||||||
"assets/vendors-node_modules_toastify-js_src_toastify_js.js": "http://localhost:8080/assets/vendors-node_modules_toastify-js_src_toastify_js.js",
|
"assets/vendors-node_modules_toastify-js_src_toastify_js.js": "http://localhost:8080/assets/vendors-node_modules_toastify-js_src_toastify_js.js",
|
||||||
"assets/vendors-node_modules_buffer_index_js-node_modules_leaflet_src_layer_tile_TileLayer_WMS_js-nod-e7bc71.js": "http://localhost:8080/assets/vendors-node_modules_buffer_index_js-node_modules_leaflet_src_layer_tile_TileLayer_WMS_js-nod-e7bc71.js",
|
"assets/vendors-node_modules_buffer_index_js-node_modules_vuedraggable_dist_vuedraggable_umd_js.js": "http://localhost:8080/assets/vendors-node_modules_buffer_index_js-node_modules_vuedraggable_dist_vuedraggable_umd_js.js",
|
||||||
"assets/vendors-node_modules_numeral_numeral_js-node_modules_chart_js_dist_chart_js.js": "http://localhost:8080/assets/vendors-node_modules_numeral_numeral_js-node_modules_chart_js_dist_chart_js.js",
|
"assets/vendors-node_modules_numeral_numeral_js-node_modules_chart_js_dist_chart_js.js": "http://localhost:8080/assets/vendors-node_modules_numeral_numeral_js-node_modules_chart_js_dist_chart_js.js",
|
||||||
"assets/resources_js_Components_BaseButton_vue.js": "http://localhost:8080/assets/resources_js_Components_BaseButton_vue.js",
|
"assets/resources_js_Components_BaseButton_vue.js": "http://localhost:8080/assets/resources_js_Components_BaseButton_vue.js",
|
||||||
"assets/resources_js_Stores_main_ts-resources_js_Components_BaseDivider_vue-resources_js_Components_C-b45805.js": "http://localhost:8080/assets/resources_js_Stores_main_ts-resources_js_Components_BaseDivider_vue-resources_js_Components_C-b45805.js",
|
"assets/resources_js_Stores_main_ts-resources_js_Components_BaseDivider_vue-resources_js_Components_C-b45805.js": "http://localhost:8080/assets/resources_js_Stores_main_ts-resources_js_Components_BaseDivider_vue-resources_js_Components_C-b45805.js",
|
||||||
|
@ -64,6 +65,7 @@
|
||||||
"assets/resources_js_Components_Admin_Sort_vue-resources_js_Components_SectionTitleLineWithButton_vue.js": "http://localhost:8080/assets/resources_js_Components_Admin_Sort_vue-resources_js_Components_SectionTitleLineWithButton_vue.js",
|
"assets/resources_js_Components_Admin_Sort_vue-resources_js_Components_SectionTitleLineWithButton_vue.js": "http://localhost:8080/assets/resources_js_Components_Admin_Sort_vue-resources_js_Components_SectionTitleLineWithButton_vue.js",
|
||||||
"assets/resources_js_Components_CardBoxModal_vue.js": "http://localhost:8080/assets/resources_js_Components_CardBoxModal_vue.js",
|
"assets/resources_js_Components_CardBoxModal_vue.js": "http://localhost:8080/assets/resources_js_Components_CardBoxModal_vue.js",
|
||||||
"assets/resources_js_Components_FileUpload_vue-resources_js_Components_FormCheckRadioGroup_vue-resour-bdf2f9.js": "http://localhost:8080/assets/resources_js_Components_FileUpload_vue-resources_js_Components_FormCheckRadioGroup_vue-resour-bdf2f9.js",
|
"assets/resources_js_Components_FileUpload_vue-resources_js_Components_FormCheckRadioGroup_vue-resour-bdf2f9.js": "http://localhost:8080/assets/resources_js_Components_FileUpload_vue-resources_js_Components_FormCheckRadioGroup_vue-resour-bdf2f9.js",
|
||||||
|
"assets/resources_js_Components_SectionTitleLineWithButton_vue-resources_js_Components_SimplePassword-945989.js": "http://localhost:8080/assets/resources_js_Components_SectionTitleLineWithButton_vue-resources_js_Components_SimplePassword-945989.js",
|
||||||
"assets/fonts/inter-latin-ext-400-normal.woff": "http://localhost:8080/assets/fonts/inter-latin-ext-400-normal.40b3b0d5.woff",
|
"assets/fonts/inter-latin-ext-400-normal.woff": "http://localhost:8080/assets/fonts/inter-latin-ext-400-normal.40b3b0d5.woff",
|
||||||
"assets/fonts/inter-latin-ext-400-normal.woff2": "http://localhost:8080/assets/fonts/inter-latin-ext-400-normal.0f9e8d4e.woff2",
|
"assets/fonts/inter-latin-ext-400-normal.woff2": "http://localhost:8080/assets/fonts/inter-latin-ext-400-normal.0f9e8d4e.woff2",
|
||||||
"assets/fonts/inter-latin-400-normal.woff": "http://localhost:8080/assets/fonts/inter-latin-400-normal.08a02fd2.woff",
|
"assets/fonts/inter-latin-400-normal.woff": "http://localhost:8080/assets/fonts/inter-latin-400-normal.08a02fd2.woff",
|
||||||
|
@ -85,5 +87,8 @@
|
||||||
"assets/images/marker-icon.png": "http://localhost:8080/assets/images/marker-icon.2b3e1faf.png",
|
"assets/images/marker-icon.png": "http://localhost:8080/assets/images/marker-icon.2b3e1faf.png",
|
||||||
"assets/images/layers-2x.png": "http://localhost:8080/assets/images/layers-2x.8f2c4d11.png",
|
"assets/images/layers-2x.png": "http://localhost:8080/assets/images/layers-2x.8f2c4d11.png",
|
||||||
"assets/images/layers.png": "http://localhost:8080/assets/images/layers.416d9136.png",
|
"assets/images/layers.png": "http://localhost:8080/assets/images/layers.416d9136.png",
|
||||||
"assets/images/Close.svg": "http://localhost:8080/assets/images/Close.e4887675.svg"
|
"assets/images/Close.svg": "http://localhost:8080/assets/images/Close.e4887675.svg",
|
||||||
|
"assets/vendors-node_modules_vue-facing-decorator_dist_esm_index_js-node_modules_vue-facing-decorator-818045.js": "http://localhost:8080/assets/vendors-node_modules_vue-facing-decorator_dist_esm_index_js-node_modules_vue-facing-decorator-818045.js",
|
||||||
|
"assets/resources_js_Pages_Admin_User_Create_vue-resources_js_Components_SimplePasswordMeter_password-f3312a.css": "http://localhost:8080/assets/resources_js_Pages_Admin_User_Create_vue-resources_js_Components_SimplePasswordMeter_password-f3312a.css",
|
||||||
|
"assets/resources_js_Pages_Admin_User_Create_vue-resources_js_Components_SimplePasswordMeter_password-f3312a.js": "http://localhost:8080/assets/resources_js_Pages_Admin_User_Create_vue-resources_js_Components_SimplePasswordMeter_password-f3312a.js"
|
||||||
}
|
}
|
|
@ -7,7 +7,7 @@ import { canvas } from 'leaflet/src/layer/vector/Canvas';
|
||||||
import { svg } from 'leaflet/src/layer/vector/SVG';
|
import { svg } from 'leaflet/src/layer/vector/SVG';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { LatLngBoundsExpression } from 'leaflet/src/geo/LatLngBounds';
|
import { LatLngBoundsExpression } from 'leaflet/src/geo/LatLngBounds';
|
||||||
// import { tileLayerWMS } from 'leaflet/src/layer/tile/TileLayer.WMS';
|
import { tileLayerWMS } from 'leaflet/src/layer/tile/TileLayer.WMS';
|
||||||
import { TileLayer } from 'leaflet/src/layer/tile/TileLayer';
|
import { TileLayer } from 'leaflet/src/layer/tile/TileLayer';
|
||||||
import { Attribution } from 'leaflet/src/control/Control.Attribution';
|
import { Attribution } from 'leaflet/src/control/Control.Attribution';
|
||||||
import DrawControlComponent from '@/Components/Map/draw.component.vue';
|
import DrawControlComponent from '@/Components/Map/draw.component.vue';
|
||||||
|
@ -60,7 +60,7 @@ Map.include({
|
||||||
const DEFAULT_BASE_LAYER_NAME = 'BaseLayer';
|
const DEFAULT_BASE_LAYER_NAME = 'BaseLayer';
|
||||||
const DEFAULT_BASE_LAYER_ATTRIBUTION = '© <a target="_blank" href="http://osm.org/copyright">OpenStreetMap</a> contributors';
|
const DEFAULT_BASE_LAYER_ATTRIBUTION = '© <a target="_blank" href="http://osm.org/copyright">OpenStreetMap</a> contributors';
|
||||||
// const OPENSEARCH_HOST = 'http://localhost:9200';
|
// const OPENSEARCH_HOST = 'http://localhost:9200';
|
||||||
const OPENSEARCH_HOST = 'http://192.168.21.18';
|
const OPENSEARCH_HOST = `${process.env.OPENSEARCH_HOST}`;
|
||||||
// const OPENSEARCH_HOST = `http://${process.env.OPENSEARCH_PUBLIC_HOST}`;
|
// const OPENSEARCH_HOST = `http://${process.env.OPENSEARCH_PUBLIC_HOST}`;
|
||||||
let map: Map;
|
let map: Map;
|
||||||
|
|
||||||
|
@ -134,21 +134,21 @@ const initMap = async () => {
|
||||||
const attributionControl = new Attribution().addTo(map);
|
const attributionControl = new Attribution().addTo(map);
|
||||||
attributionControl.setPrefix(false);
|
attributionControl.setPrefix(false);
|
||||||
|
|
||||||
// let osmGgray = tileLayerWMS('https://ows.terrestris.de/osm-gray/service', {
|
let osmGgray = tileLayerWMS('https://ows.terrestris.de/osm-gray/service', {
|
||||||
// format: 'image/png',
|
format: 'image/png',
|
||||||
// attribution: DEFAULT_BASE_LAYER_ATTRIBUTION,
|
|
||||||
// layers: 'OSM-WMS',
|
|
||||||
// });
|
|
||||||
|
|
||||||
let baseAt = new TileLayer('https://{s}.wien.gv.at/basemap/bmapgrau/normal/google3857/{z}/{y}/{x}.png', {
|
|
||||||
subdomains: ['maps', 'maps1', 'maps2', 'maps3', 'maps4'],
|
|
||||||
attribution: DEFAULT_BASE_LAYER_ATTRIBUTION,
|
attribution: DEFAULT_BASE_LAYER_ATTRIBUTION,
|
||||||
|
layers: 'OSM-WMS',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// let baseAt = new TileLayer('https://{s}.wien.gv.at/basemap/bmapgrau/normal/google3857/{z}/{y}/{x}.png', {
|
||||||
|
// subdomains: ['maps', 'maps1', 'maps2', 'maps3', 'maps4'],
|
||||||
|
// attribution: DEFAULT_BASE_LAYER_ATTRIBUTION,
|
||||||
|
// });
|
||||||
|
|
||||||
let layerOptions = {
|
let layerOptions = {
|
||||||
label: DEFAULT_BASE_LAYER_NAME,
|
label: DEFAULT_BASE_LAYER_NAME,
|
||||||
visible: true,
|
visible: true,
|
||||||
layer: baseAt,
|
layer: osmGgray,
|
||||||
};
|
};
|
||||||
layerOptions.layer.addTo(map);
|
layerOptions.layer.addTo(map);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
// common passwords as an array of strings
|
||||||
|
|
||||||
|
const commonPasswords = [
|
||||||
|
'123456',
|
||||||
|
'qwerty',
|
||||||
|
'password',
|
||||||
|
'111111',
|
||||||
|
'Abc123',
|
||||||
|
'123456789',
|
||||||
|
'12345678',
|
||||||
|
'123123',
|
||||||
|
'1234567890',
|
||||||
|
'12345',
|
||||||
|
'1234567',
|
||||||
|
'qwertyuiop',
|
||||||
|
'qwerty123',
|
||||||
|
'1q2w3e',
|
||||||
|
'password1',
|
||||||
|
'123321',
|
||||||
|
'Iloveyou',
|
||||||
|
'12345',
|
||||||
|
'test',
|
||||||
|
'test007'
|
||||||
|
];
|
||||||
|
|
||||||
|
export default commonPasswords;
|
|
@ -0,0 +1,9 @@
|
||||||
|
export default class TrieNode {
|
||||||
|
children: { [key: string]: TrieNode };
|
||||||
|
isEndOfWord: boolean;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.children = {};
|
||||||
|
this.isEndOfWord = false;
|
||||||
|
}
|
||||||
|
}
|
30
resources/js/Components/SimplePasswordMeter/logic/Trie.ts
Normal file
30
resources/js/Components/SimplePasswordMeter/logic/Trie.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import TrieNode from './TieNode';
|
||||||
|
|
||||||
|
export default class Trie {
|
||||||
|
private root: TrieNode;
|
||||||
|
constructor() {
|
||||||
|
this.root = new TrieNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
insert(word: string) {
|
||||||
|
let node: TrieNode = this.root;
|
||||||
|
for (let char of word) {
|
||||||
|
if (!node.children[char]) {
|
||||||
|
node.children[char] = new TrieNode();
|
||||||
|
}
|
||||||
|
node = node.children[char];
|
||||||
|
}
|
||||||
|
node.isEndOfWord = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
search(word: string) {
|
||||||
|
let node = this.root;
|
||||||
|
for (let char of word) {
|
||||||
|
if (!node.children[char]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
node = node.children[char];
|
||||||
|
}
|
||||||
|
return node.isEndOfWord;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
import commonPasswords from '../data/commonPasswords';
|
||||||
|
import Trie from './Trie';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const checkStrength = (pass: string) => {
|
||||||
|
const score = scorePassword(pass);
|
||||||
|
const scoreLabel = mapScoreToLabel(score);
|
||||||
|
return {
|
||||||
|
score,
|
||||||
|
scoreLabel
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default checkStrength;
|
||||||
|
|
||||||
|
// Function to score the password based on different criteria
|
||||||
|
const scorePassword = (password: string): number => {
|
||||||
|
if (password.length <= 6) return 0;
|
||||||
|
if (isCommonPassword(password)) return 0;
|
||||||
|
|
||||||
|
let score = 0;
|
||||||
|
score += getLengthScore(password);
|
||||||
|
score += getSpecialCharScore(password);
|
||||||
|
score += getCaseMixScore(password);
|
||||||
|
score += getNumberMixScore(password);
|
||||||
|
|
||||||
|
return Math.min(score, 4); // Maximum score is 4
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize the Trie with common passwords
|
||||||
|
const trie = new Trie();
|
||||||
|
commonPasswords.forEach(password => trie.insert(password));
|
||||||
|
const isCommonPassword = (password: string): boolean => {
|
||||||
|
// return commonPasswords.includes(password);
|
||||||
|
return trie.search(password);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to get the score based on password length
|
||||||
|
const getLengthScore = (password: string): number => {
|
||||||
|
if (password.length > 20 && !hasRepeatChars(password)) return 3;
|
||||||
|
if (password.length > 12 && !hasRepeatChars(password)) return 2;
|
||||||
|
if (password.length > 8) return 1;
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
// Function to check if the password contains repeated characters
|
||||||
|
const hasRepeatChars = (password: string): boolean => {
|
||||||
|
const repeatCharRegex = /(\w)(\1+\1+\1+\1+)/g;
|
||||||
|
return repeatCharRegex.test(password);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to get the score based on the presence of special characters
|
||||||
|
const getSpecialCharScore = (password: string): number => {
|
||||||
|
const specialCharRegex = /[^A-Za-z0-9]/g;
|
||||||
|
return specialCharRegex.test(password) ? 1 : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to get the score based on the mix of uppercase and lowercase letters
|
||||||
|
const getCaseMixScore = (password: string): number => {
|
||||||
|
const hasUpperCase = /[A-Z]/.test(password);
|
||||||
|
const hasLowerCase = /[a-z]/.test(password);
|
||||||
|
return hasUpperCase && hasLowerCase ? 1 : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to get the score based on the mix of letters and numbers
|
||||||
|
const getNumberMixScore = (password: string): number => {
|
||||||
|
const hasLetter = /[A-Za-z]/.test(password);
|
||||||
|
const hasNumber = /[0-9]/.test(password);
|
||||||
|
return hasLetter && hasNumber ? 1 : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to map the score to a corresponding label
|
||||||
|
const mapScoreToLabel = (score: number): string => {
|
||||||
|
const labels = ['risky', 'guessable', 'weak', 'safe', 'secure'];
|
||||||
|
return labels[score] || '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// const nameScore = (score: number): string => {
|
||||||
|
// switch (score) {
|
||||||
|
// case 0:
|
||||||
|
// return 'risky';
|
||||||
|
// case 1:
|
||||||
|
// return 'guessable';
|
||||||
|
// case 2:
|
||||||
|
// return 'weak';
|
||||||
|
// case 3:
|
||||||
|
// return 'safe';
|
||||||
|
// case 4:
|
||||||
|
// return 'secure';
|
||||||
|
// default:
|
||||||
|
// return '';
|
||||||
|
// }
|
||||||
|
// };
|
|
@ -0,0 +1,5 @@
|
||||||
|
// import scorePassword from './scorePassword'
|
||||||
|
// import nameScore from './nameScore'
|
||||||
|
import checkStrength from './checkStrength'
|
||||||
|
|
||||||
|
export { checkStrength }
|
|
@ -0,0 +1,66 @@
|
||||||
|
// import { isCommonPassword } from "./isCommonPassword"
|
||||||
|
import commonPasswords from '../data/commonPasswords';
|
||||||
|
|
||||||
|
const scorePassword = (pass: string): number => {
|
||||||
|
let score = 0;
|
||||||
|
let length = 0;
|
||||||
|
let specialChar = 0;
|
||||||
|
let caseMix = 0;
|
||||||
|
let numCharMix = 0;
|
||||||
|
|
||||||
|
const specialCharRegex = /[^A-Za-z0-9]/g;
|
||||||
|
const lowercaseRegex = /(.*[a-z].*)/g;
|
||||||
|
const uppercaseRegex = /(.*[A-Z].*)/g;
|
||||||
|
const numberRegex = /(.*[0-9].*)/g;
|
||||||
|
const repeatCharRegex = /(\w)(\1+\1+\1+\1+)/g;
|
||||||
|
|
||||||
|
const hasSpecialChar = specialCharRegex.test(pass);
|
||||||
|
const hasLowerCase = lowercaseRegex.test(pass);
|
||||||
|
const hasUpperCase = uppercaseRegex.test(pass);
|
||||||
|
const hasNumber = numberRegex.test(pass);
|
||||||
|
const hasRepeatChars = repeatCharRegex.test(pass);
|
||||||
|
|
||||||
|
if (pass.length > 4) {
|
||||||
|
if (isCommonPassword(pass)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((hasLowerCase || hasUpperCase) && hasNumber) {
|
||||||
|
numCharMix = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasUpperCase && hasLowerCase) {
|
||||||
|
caseMix = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((hasLowerCase || hasUpperCase || hasNumber) && hasSpecialChar) {
|
||||||
|
specialChar = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pass.length > 8) {
|
||||||
|
length = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pass.length > 12 && !hasRepeatChars) {
|
||||||
|
length = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pass.length > 20 && !hasRepeatChars) {
|
||||||
|
length = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
score = length + specialChar + caseMix + numCharMix;
|
||||||
|
|
||||||
|
if (score > 4) {
|
||||||
|
score = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default scorePassword;
|
||||||
|
|
||||||
|
const isCommonPassword = (password: string): boolean => {
|
||||||
|
return commonPasswords.includes(password);
|
||||||
|
};
|
|
@ -0,0 +1,95 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { checkStrength } from './logic/index';
|
||||||
|
|
||||||
|
// Define props
|
||||||
|
const props = defineProps<{
|
||||||
|
password: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// Define emits
|
||||||
|
// const emit = defineEmits<{
|
||||||
|
// (event: 'score', payload: { score: number; strength: string }): void;
|
||||||
|
// }>();
|
||||||
|
|
||||||
|
const emit = defineEmits(['score']);
|
||||||
|
|
||||||
|
// const score = (event) => {
|
||||||
|
// emit('score', event, payload: { score; strength: string });
|
||||||
|
// };
|
||||||
|
|
||||||
|
// Computed property for password class
|
||||||
|
const passwordClass = computed(() => {
|
||||||
|
if (!props.password) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// const scoreLabel = checkStrength(props.password);
|
||||||
|
// const score = scorePassword(props.password);
|
||||||
|
const { score, scoreLabel } = checkStrength(props.password);
|
||||||
|
emit('score', score);
|
||||||
|
return {
|
||||||
|
[scoreLabel]: true,
|
||||||
|
// scored: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// export default {
|
||||||
|
// name: 'PasswordMeter',
|
||||||
|
// props: {
|
||||||
|
// password: String,
|
||||||
|
// },
|
||||||
|
// emits: ['score'],
|
||||||
|
// computed: {
|
||||||
|
// passwordClass(): object | null {
|
||||||
|
// if (!this.password) {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
// const strength = checkStrength(this.password);
|
||||||
|
// const score = scorePassword(this.password);
|
||||||
|
// this.$emit('score', { score, strength });
|
||||||
|
// return {
|
||||||
|
// [strength]: true,
|
||||||
|
// scored: true,
|
||||||
|
// };
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="po-password-strength-bar" :class="passwordClass" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
.po-password-strength-bar {
|
||||||
|
border-radius: 2px;
|
||||||
|
transition: all 0.2s linear;
|
||||||
|
height: 10px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.po-password-strength-bar.risky {
|
||||||
|
background-color: #f95e68;
|
||||||
|
width: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.po-password-strength-bar.guessable {
|
||||||
|
background-color: #fb964d;
|
||||||
|
width: 32.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.po-password-strength-bar.weak {
|
||||||
|
background-color: #fdd244;
|
||||||
|
width: 55%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.po-password-strength-bar.safe {
|
||||||
|
background-color: #b0dc53;
|
||||||
|
width: 77.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.po-password-strength-bar.secure {
|
||||||
|
background-color: #35cc62;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,4 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
import { Head, useForm, router } from '@inertiajs/vue3';
|
import { Head, useForm, router } from '@inertiajs/vue3';
|
||||||
import { mdiAccountKey, mdiArrowLeftBoldOutline } from '@mdi/js';
|
import { mdiAccountKey, mdiArrowLeftBoldOutline } from '@mdi/js';
|
||||||
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
|
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
|
||||||
|
@ -13,6 +14,16 @@ import BaseButton from '@/Components/BaseButton.vue';
|
||||||
import BaseButtons from '@/Components/BaseButtons.vue';
|
import BaseButtons from '@/Components/BaseButtons.vue';
|
||||||
import { stardust } from '@eidellev/adonis-stardust/client';
|
import { stardust } from '@eidellev/adonis-stardust/client';
|
||||||
// import { Inertia } from '@inertiajs/inertia';
|
// import { Inertia } from '@inertiajs/inertia';
|
||||||
|
import passwordMeter from '@/Components/SimplePasswordMeter/password-meter.vue';
|
||||||
|
|
||||||
|
const enabled = ref(false);
|
||||||
|
const handleScore = (score: number) => {
|
||||||
|
if (score == 4){
|
||||||
|
enabled.value = true;
|
||||||
|
} else {
|
||||||
|
enabled.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
roles: {
|
roles: {
|
||||||
|
@ -80,6 +91,7 @@ const submit = async () => {
|
||||||
</div>
|
</div>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
<password-meter :password="form.password" @score="handleScore" />
|
||||||
|
|
||||||
<FormField label="Password Confirmation" :class="{ 'text-red-400': errors.password_confirmation }">
|
<FormField label="Password Confirmation" :class="{ 'text-red-400': errors.password_confirmation }">
|
||||||
<FormControl
|
<FormControl
|
||||||
|
@ -114,8 +126,8 @@ const submit = async () => {
|
||||||
type="submit"
|
type="submit"
|
||||||
color="info"
|
color="info"
|
||||||
label="Submit"
|
label="Submit"
|
||||||
:class="{ 'opacity-25': form.processing }"
|
:class="{ 'opacity-25': form.processing }"
|
||||||
:disabled="form.processing"
|
:disabled="form.processing == true || enabled == false"
|
||||||
/>
|
/>
|
||||||
</BaseButtons>
|
</BaseButtons>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
import { Head, useForm, router } from '@inertiajs/vue3';
|
import { Head, useForm, router } from '@inertiajs/vue3';
|
||||||
import { mdiAccountKey, mdiArrowLeftBoldOutline } from '@mdi/js';
|
import { mdiAccountKey, mdiArrowLeftBoldOutline } from '@mdi/js';
|
||||||
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
|
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
|
||||||
|
@ -13,7 +14,9 @@ import BaseButton from '@/Components/BaseButton.vue';
|
||||||
import BaseButtons from '@/Components/BaseButtons.vue';
|
import BaseButtons from '@/Components/BaseButtons.vue';
|
||||||
import { stardust } from '@eidellev/adonis-stardust/client';
|
import { stardust } from '@eidellev/adonis-stardust/client';
|
||||||
// import { Inertia } from '@inertiajs/inertia';
|
// import { Inertia } from '@inertiajs/inertia';
|
||||||
|
import passwordMeter from '@/Components/SimplePasswordMeter/password-meter.vue';
|
||||||
|
|
||||||
|
const enabled = ref(false);
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
user: {
|
user: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -46,6 +49,15 @@ const submit = async () => {
|
||||||
// await Inertia.post(stardust.route('user.store'), form);
|
// await Inertia.post(stardust.route('user.store'), form);
|
||||||
await router.put(stardust.route('settings.user.update', [props.user.id]), form);
|
await router.put(stardust.route('settings.user.update', [props.user.id]), form);
|
||||||
};
|
};
|
||||||
|
const handleScore = (score: number) => {
|
||||||
|
if (score == 4){
|
||||||
|
enabled.value = true;
|
||||||
|
} else {
|
||||||
|
enabled.value = false;
|
||||||
|
}
|
||||||
|
// strengthLabel.value = scoreLabel;
|
||||||
|
// score.value = scoreValue;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -88,6 +100,8 @@ const submit = async () => {
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
|
<password-meter :password="form.password" @score="handleScore" />
|
||||||
|
|
||||||
<FormField label="Password Confirmation" :class="{ 'text-red-400': errors.password_confirmation }">
|
<FormField label="Password Confirmation" :class="{ 'text-red-400': errors.password_confirmation }">
|
||||||
<FormControl
|
<FormControl
|
||||||
v-model="form.password_confirmation"
|
v-model="form.password_confirmation"
|
||||||
|
@ -121,7 +135,7 @@ const submit = async () => {
|
||||||
color="info"
|
color="info"
|
||||||
label="Submit"
|
label="Submit"
|
||||||
:class="{ 'opacity-25': form.processing }"
|
:class="{ 'opacity-25': form.processing }"
|
||||||
:disabled="form.processing"
|
:disabled="form.processing == true || enabled == false"
|
||||||
/>
|
/>
|
||||||
</BaseButtons>
|
</BaseButtons>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// import { Head, Link, useForm } from '@inertiajs/inertia-vue3';
|
// import { Head, Link, useForm } from '@inertiajs/inertia-vue3';
|
||||||
|
import { ref } from 'vue';
|
||||||
import { useForm } from '@inertiajs/vue3';
|
import { useForm } from '@inertiajs/vue3';
|
||||||
// import { ref } from 'vue';
|
// import { ref } from 'vue';
|
||||||
// import { reactive } from 'vue';
|
// import { reactive } from 'vue';
|
||||||
|
@ -24,7 +25,7 @@ import NotificationBar from '@/Components/NotificationBar.vue';
|
||||||
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
|
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
|
||||||
import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue';
|
import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue';
|
||||||
import { stardust } from '@eidellev/adonis-stardust/client';
|
import { stardust } from '@eidellev/adonis-stardust/client';
|
||||||
// import { Inertia } from '@inertiajs/inertia';
|
import passwordMeter from '@/Components/SimplePasswordMeter/password-meter.vue';
|
||||||
import { computed, Ref } from 'vue';
|
import { computed, Ref } from 'vue';
|
||||||
import { usePage } from '@inertiajs/vue3';
|
import { usePage } from '@inertiajs/vue3';
|
||||||
import FormValidationErrors from '@/Components/FormValidationErrors.vue';
|
import FormValidationErrors from '@/Components/FormValidationErrors.vue';
|
||||||
|
@ -35,6 +36,17 @@ import PersonalTotpSettings from '@/Components/PersonalTotpSettings.vue';
|
||||||
|
|
||||||
const emit = defineEmits(['confirm', 'update:confirmation'])
|
const emit = defineEmits(['confirm', 'update:confirmation'])
|
||||||
|
|
||||||
|
const enabled = ref(false);
|
||||||
|
const handleScore = (score: number) => {
|
||||||
|
if (score == 4){
|
||||||
|
enabled.value = true;
|
||||||
|
} else {
|
||||||
|
enabled.value = false;
|
||||||
|
}
|
||||||
|
// strengthLabel.value = scoreLabel;
|
||||||
|
// score.value = scoreValue;
|
||||||
|
};
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
// user will be returned from controller action
|
// user will be returned from controller action
|
||||||
user: {
|
user: {
|
||||||
|
@ -183,6 +195,8 @@ const flash: Ref<any> = computed(() => {
|
||||||
</div>
|
</div>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
<password-meter :password="passwordForm.new_password" @score="handleScore" />
|
||||||
|
|
||||||
|
|
||||||
<FormField label="Confirm password" help="Required. New password one more time"
|
<FormField label="Confirm password" help="Required. New password one more time"
|
||||||
:class="{ 'text-red-400': passwordForm.errors.confirm_password }">
|
:class="{ 'text-red-400': passwordForm.errors.confirm_password }">
|
||||||
|
@ -205,7 +219,7 @@ const flash: Ref<any> = computed(() => {
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<BaseButtons>
|
<BaseButtons>
|
||||||
<BaseButton type="submit" color="info" label="Change password" />
|
<BaseButton type="submit" color="info" label="Change password" :disabled="passwordForm.processing == true || enabled == false" />
|
||||||
</BaseButtons>
|
</BaseButtons>
|
||||||
</template>
|
</template>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user