nuxt-users 1.11.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/dist/cli.mjs +2 -3
- package/dist/module.d.mts +1 -1
- package/dist/module.d.ts +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +30 -9
- package/dist/runtime/assets/nuxt-users.css +1 -0
- package/dist/runtime/components/NUsersList.vue +136 -0
- package/dist/runtime/components/NUsersList.vue.d.ts +74 -0
- package/dist/runtime/components/{LoginForm.vue → NUsersLoginForm.vue} +20 -23
- package/dist/runtime/components/{LoginForm.vue.d.ts → NUsersLoginForm.vue.d.ts} +5 -9
- package/dist/runtime/components/{LogoutLink.vue → NUsersLogoutLink.vue} +2 -6
- package/dist/runtime/components/{PasswordStrengthIndicator.vue → NUsersPasswordStrengthIndicator.vue} +18 -22
- package/dist/runtime/components/{ProfileInfo.vue → NUsersProfileInfo.vue} +5 -7
- package/dist/runtime/components/{ResetPasswordForm.vue → NUsersResetPasswordForm.vue} +22 -26
- package/dist/runtime/components/{ResetPasswordForm.vue.d.ts → NUsersResetPasswordForm.vue.d.ts} +3 -6
- package/dist/runtime/components/NUsersUserCard.vue +93 -0
- package/dist/runtime/components/NUsersUserCard.vue.d.ts +33 -0
- package/dist/runtime/components/NUsersUserForm.vue +141 -0
- package/dist/runtime/components/NUsersUserForm.vue.d.ts +12 -0
- package/dist/runtime/composables/useAuthentication.d.ts +2 -0
- package/dist/runtime/composables/usePasswordValidation.d.ts +2 -2
- package/dist/runtime/server/api/nuxt-users/index.get.d.ts +20 -0
- package/dist/runtime/server/api/nuxt-users/index.get.js +58 -0
- package/dist/runtime/server/api/nuxt-users/session/index.post.d.ts +1 -0
- package/dist/runtime/server/middleware/authorization.server.js +23 -8
- package/dist/runtime/server/utils/user.js +8 -5
- package/dist/shared/{nuxt-users.C7T1DKBV.d.mts → nuxt-users.B5ttEpIO.d.mts} +1 -0
- package/dist/shared/{nuxt-users.C7T1DKBV.d.ts → nuxt-users.B5ttEpIO.d.ts} +1 -0
- package/dist/utils.d.mts +1 -2
- package/dist/utils.d.ts +1 -2
- package/dist/utils.mjs +162 -2
- package/package.json +1 -1
- package/dist/runtime/assets/colors.css +0 -1
- package/dist/shared/nuxt-users.BnOQs2LI.mjs +0 -162
- package/dist/runtime/components/{LogoutLink.vue.d.ts → NUsersLogoutLink.vue.d.ts} +4 -4
- /package/dist/runtime/components/{PasswordStrengthIndicator.vue.d.ts → NUsersPasswordStrengthIndicator.vue.d.ts} +0 -0
- /package/dist/runtime/components/{ProfileInfo.vue.d.ts → NUsersProfileInfo.vue.d.ts} +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { createError, defineEventHandler, getQuery } from "h3";
|
|
2
|
+
import { useRuntimeConfig } from "#imports";
|
|
3
|
+
import { useDb } from "../../utils/db.js";
|
|
4
|
+
import { getLastLoginTime } from "../../utils/user.js";
|
|
5
|
+
export default defineEventHandler(async (event) => {
|
|
6
|
+
const { nuxtUsers } = useRuntimeConfig();
|
|
7
|
+
const options = nuxtUsers;
|
|
8
|
+
const db = await useDb(options);
|
|
9
|
+
const usersTable = options.tables.users;
|
|
10
|
+
const query = getQuery(event);
|
|
11
|
+
const page = Number(query.page) || 1;
|
|
12
|
+
const limit = Number(query.limit) || 10;
|
|
13
|
+
const offset = (page - 1) * limit;
|
|
14
|
+
if (page < 1 || limit < 1 || limit > 100) {
|
|
15
|
+
throw createError({
|
|
16
|
+
statusCode: 400,
|
|
17
|
+
statusMessage: "Invalid pagination parameters. Page must be >= 1, limit must be between 1 and 100."
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const countResult = await db.sql`SELECT COUNT(*) as total FROM {${usersTable}}`;
|
|
22
|
+
const total = countResult.rows[0].total;
|
|
23
|
+
const usersResult = await db.sql`
|
|
24
|
+
SELECT id, email, name, role, created_at, updated_at
|
|
25
|
+
FROM {${usersTable}}
|
|
26
|
+
ORDER BY name ASC
|
|
27
|
+
LIMIT ${limit} OFFSET ${offset}
|
|
28
|
+
`;
|
|
29
|
+
const users = await Promise.all(usersResult.rows.map(async (user) => {
|
|
30
|
+
const lastLoginTime = await getLastLoginTime(user.id, options);
|
|
31
|
+
return {
|
|
32
|
+
id: user.id,
|
|
33
|
+
email: user.email,
|
|
34
|
+
name: user.name,
|
|
35
|
+
role: user.role,
|
|
36
|
+
created_at: user.created_at instanceof Date ? user.created_at.toISOString() : user.created_at,
|
|
37
|
+
updated_at: user.updated_at instanceof Date ? user.updated_at.toISOString() : user.updated_at,
|
|
38
|
+
last_login_at: lastLoginTime
|
|
39
|
+
};
|
|
40
|
+
}));
|
|
41
|
+
return {
|
|
42
|
+
users,
|
|
43
|
+
pagination: {
|
|
44
|
+
page,
|
|
45
|
+
limit,
|
|
46
|
+
total,
|
|
47
|
+
totalPages: Math.ceil(total / limit),
|
|
48
|
+
hasNext: page * limit < total,
|
|
49
|
+
hasPrev: page > 1
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
} catch (error) {
|
|
53
|
+
throw createError({
|
|
54
|
+
statusCode: 500,
|
|
55
|
+
statusMessage: `Error fetching users: ${error instanceof Error ? error.message : String(error)}`
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineEventHandler, getCookie,
|
|
1
|
+
import { defineEventHandler, getCookie, createError } from "h3";
|
|
2
2
|
import { useRuntimeConfig } from "#imports";
|
|
3
3
|
import { getCurrentUserFromToken } from "../utils/index.js";
|
|
4
4
|
import { hasPermission, isWhitelisted } from "../../utils/permissions.js";
|
|
@@ -12,7 +12,7 @@ export default defineEventHandler(async (event) => {
|
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
if (NO_AUTH_PATHS.includes(event.path)) {
|
|
15
|
-
console.log(
|
|
15
|
+
console.log(`[Nuxt Users] server.middleware.auth.global: NO_AUTH_PATH: ${event.path}`);
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
18
|
const openApiPaths = NO_AUTH_API_PATHS.map((path) => `${base}${path}`);
|
|
@@ -25,17 +25,32 @@ export default defineEventHandler(async (event) => {
|
|
|
25
25
|
}
|
|
26
26
|
const token = getCookie(event, "auth_token");
|
|
27
27
|
if (!token) {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
if (event.path.startsWith("/api/")) {
|
|
29
|
+
console.log(`[Nuxt Users] server.middleware.auth.global: ${event.path} No token found - API request rejected`);
|
|
30
|
+
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
31
|
+
} else {
|
|
32
|
+
console.log(`[Nuxt Users] server.middleware.auth.global: ${event.path} No token found - letting client handle page redirect`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
30
35
|
}
|
|
31
36
|
const user = await getCurrentUserFromToken(token, options);
|
|
32
37
|
if (!user) {
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
if (event.path.startsWith("/api/")) {
|
|
39
|
+
console.log(`[Nuxt Users] server.middleware.auth.global: ${event.path} Invalid token - API request rejected`);
|
|
40
|
+
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
41
|
+
} else {
|
|
42
|
+
console.log(`[Nuxt Users] server.middleware.auth.global: ${event.path} Invalid token - letting client handle page redirect`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
35
45
|
}
|
|
36
46
|
if (!hasPermission(user.role, event.path, options.auth.permissions)) {
|
|
37
|
-
|
|
38
|
-
|
|
47
|
+
if (event.path.startsWith("/api/")) {
|
|
48
|
+
console.log(`[Nuxt Users] server.middleware.auth.global: ${event.path} User ${user.id} with role ${user.role} denied access - API request rejected`);
|
|
49
|
+
throw createError({ statusCode: 403, statusMessage: "Forbidden" });
|
|
50
|
+
} else {
|
|
51
|
+
console.log(`[Nuxt Users] server.middleware.auth.global: ${event.path} User ${user.id} with role ${user.role} denied access - letting client handle page redirect`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
39
54
|
}
|
|
40
55
|
console.log(`[Nuxt Users] server.middleware.auth.global: Authenticated request to ${event.path} for ${user.id} with role ${user.role}`);
|
|
41
56
|
return;
|
|
@@ -69,21 +69,24 @@ export const findUserById = async (id, options, withPass = false) => {
|
|
|
69
69
|
export const updateUser = async (id, userData, options) => {
|
|
70
70
|
const db = await useDb(options);
|
|
71
71
|
const usersTable = options.tables.users;
|
|
72
|
-
const currentUser = await findUserById(id, options
|
|
72
|
+
const currentUser = await findUserById(id, options);
|
|
73
73
|
if (!currentUser) {
|
|
74
74
|
throw new Error("User not found.");
|
|
75
75
|
}
|
|
76
76
|
const fieldsToUpdate = { ...userData };
|
|
77
77
|
delete fieldsToUpdate.id;
|
|
78
|
+
delete fieldsToUpdate.created_at;
|
|
79
|
+
delete fieldsToUpdate.updated_at;
|
|
80
|
+
delete fieldsToUpdate.last_login_at;
|
|
78
81
|
delete fieldsToUpdate.password;
|
|
79
82
|
if (Object.keys(fieldsToUpdate).length === 0) {
|
|
80
|
-
|
|
81
|
-
return user;
|
|
83
|
+
return currentUser;
|
|
82
84
|
}
|
|
83
|
-
|
|
85
|
+
const dirtyFields = Object.keys(fieldsToUpdate).filter((key) => currentUser[key] != fieldsToUpdate[key]);
|
|
86
|
+
for (const key of dirtyFields) {
|
|
84
87
|
await db.sql`
|
|
85
88
|
UPDATE {${usersTable}}
|
|
86
|
-
SET ${key} = ${
|
|
89
|
+
SET {${key}} = ${fieldsToUpdate[key]}, updated_at = CURRENT_TIMESTAMP
|
|
87
90
|
WHERE id = ${id}
|
|
88
91
|
`;
|
|
89
92
|
}
|
package/dist/utils.d.mts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
export { L as LoginFormData, M as ModuleOptions, b as PasswordResetToken, P as PersonalAccessToken, U as User, a as UserWithoutPassword } from './shared/nuxt-users.
|
|
2
|
-
export { findUserByEmail, getCurrentUserFromToken, getLastLoginTime } from '../dist/runtime/server/utils/user.js';
|
|
1
|
+
export { L as LoginFormData, M as ModuleOptions, b as PasswordResetToken, P as PersonalAccessToken, U as User, a as UserWithoutPassword } from './shared/nuxt-users.B5ttEpIO.mjs';
|
|
3
2
|
|
|
4
3
|
interface PasswordValidationResult {
|
|
5
4
|
isValid: boolean;
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
export { L as LoginFormData, M as ModuleOptions, b as PasswordResetToken, P as PersonalAccessToken, U as User, a as UserWithoutPassword } from './shared/nuxt-users.
|
|
2
|
-
export { findUserByEmail, getCurrentUserFromToken, getLastLoginTime } from '../dist/runtime/server/utils/user.js';
|
|
1
|
+
export { L as LoginFormData, M as ModuleOptions, b as PasswordResetToken, P as PersonalAccessToken, U as User, a as UserWithoutPassword } from './shared/nuxt-users.B5ttEpIO.js';
|
|
3
2
|
|
|
4
3
|
interface PasswordValidationResult {
|
|
5
4
|
isValid: boolean;
|
package/dist/utils.mjs
CHANGED
|
@@ -1,2 +1,162 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const DEFAULT_OPTIONS = {
|
|
2
|
+
minLength: 8,
|
|
3
|
+
requireUppercase: true,
|
|
4
|
+
requireLowercase: true,
|
|
5
|
+
requireNumbers: true,
|
|
6
|
+
requireSpecialChars: true,
|
|
7
|
+
preventCommonPasswords: true
|
|
8
|
+
};
|
|
9
|
+
const getPasswordValidationOptions = (moduleOptions) => {
|
|
10
|
+
if (!moduleOptions?.passwordValidation) {
|
|
11
|
+
return DEFAULT_OPTIONS;
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
...DEFAULT_OPTIONS,
|
|
15
|
+
...moduleOptions.passwordValidation
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
const COMMON_PASSWORDS = [
|
|
19
|
+
"password",
|
|
20
|
+
"123456",
|
|
21
|
+
"123456789",
|
|
22
|
+
"qwerty",
|
|
23
|
+
"abc123",
|
|
24
|
+
"password123",
|
|
25
|
+
"admin",
|
|
26
|
+
"letmein",
|
|
27
|
+
"welcome",
|
|
28
|
+
"monkey",
|
|
29
|
+
"dragon",
|
|
30
|
+
"master",
|
|
31
|
+
"hello",
|
|
32
|
+
"freedom",
|
|
33
|
+
"whatever",
|
|
34
|
+
"qazwsx",
|
|
35
|
+
"trustno1",
|
|
36
|
+
"jordan",
|
|
37
|
+
"harley",
|
|
38
|
+
"ranger",
|
|
39
|
+
"buster",
|
|
40
|
+
"thomas",
|
|
41
|
+
"tigger",
|
|
42
|
+
"robert",
|
|
43
|
+
"soccer",
|
|
44
|
+
"batman",
|
|
45
|
+
"test",
|
|
46
|
+
"pass",
|
|
47
|
+
"user",
|
|
48
|
+
"guest",
|
|
49
|
+
"login",
|
|
50
|
+
"secret",
|
|
51
|
+
"god",
|
|
52
|
+
"love",
|
|
53
|
+
"sex",
|
|
54
|
+
"money",
|
|
55
|
+
"password1",
|
|
56
|
+
"12345678",
|
|
57
|
+
"qwerty123",
|
|
58
|
+
"admin123",
|
|
59
|
+
"password!",
|
|
60
|
+
"password1!",
|
|
61
|
+
"password123",
|
|
62
|
+
"password123!"
|
|
63
|
+
];
|
|
64
|
+
const validatePassword = (password, options = {}) => {
|
|
65
|
+
const config = { ...DEFAULT_OPTIONS, ...options };
|
|
66
|
+
const errors = [];
|
|
67
|
+
const hints = [];
|
|
68
|
+
let score = 0;
|
|
69
|
+
if (password.length < config.minLength) {
|
|
70
|
+
errors.push(`Password must be at least ${config.minLength} characters long`);
|
|
71
|
+
hints.push(`Use at least ${config.minLength} characters`);
|
|
72
|
+
} else {
|
|
73
|
+
score += 20;
|
|
74
|
+
if (password.length < 12) {
|
|
75
|
+
hints.push("Use 12 or more characters for extra security");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (config.requireUppercase && !/[A-Z]/.test(password)) {
|
|
79
|
+
errors.push("Password must contain at least one uppercase letter");
|
|
80
|
+
hints.push("Add an uppercase letter (A-Z)");
|
|
81
|
+
} else if (/[A-Z]/.test(password)) {
|
|
82
|
+
score += 15;
|
|
83
|
+
}
|
|
84
|
+
if (config.requireLowercase && !/[a-z]/.test(password)) {
|
|
85
|
+
errors.push("Password must contain at least one lowercase letter");
|
|
86
|
+
hints.push("Add a lowercase letter (a-z)");
|
|
87
|
+
} else if (/[a-z]/.test(password)) {
|
|
88
|
+
score += 15;
|
|
89
|
+
}
|
|
90
|
+
if (config.requireNumbers && !/\d/.test(password)) {
|
|
91
|
+
errors.push("Password must contain at least one number");
|
|
92
|
+
hints.push("Add a number (0-9)");
|
|
93
|
+
} else if (/\d/.test(password)) {
|
|
94
|
+
score += 15;
|
|
95
|
+
}
|
|
96
|
+
if (config.requireSpecialChars && !/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password)) {
|
|
97
|
+
errors.push("Password must contain at least one special character");
|
|
98
|
+
hints.push("Add a special character (e.g. !@#$%)");
|
|
99
|
+
} else if (/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password)) {
|
|
100
|
+
score += 15;
|
|
101
|
+
}
|
|
102
|
+
if (config.preventCommonPasswords && COMMON_PASSWORDS.includes(password.toLowerCase())) {
|
|
103
|
+
errors.push("Password is too common. Please choose a more unique password");
|
|
104
|
+
hints.push("Avoid common passwords or names");
|
|
105
|
+
}
|
|
106
|
+
if (password.length >= 12) {
|
|
107
|
+
score += 10;
|
|
108
|
+
} else if (password.length >= 10) {
|
|
109
|
+
score += 5;
|
|
110
|
+
}
|
|
111
|
+
const hasUppercase = /[A-Z]/.test(password);
|
|
112
|
+
const hasLowercase = /[a-z]/.test(password);
|
|
113
|
+
const hasNumbers = /\d/.test(password);
|
|
114
|
+
const hasSpecial = /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password);
|
|
115
|
+
const complexityTypes = [hasUppercase, hasLowercase, hasNumbers, hasSpecial].filter(Boolean).length;
|
|
116
|
+
if (complexityTypes >= 4) {
|
|
117
|
+
score += 10;
|
|
118
|
+
} else if (complexityTypes >= 3) {
|
|
119
|
+
score += 5;
|
|
120
|
+
hints.push("Use a mix of uppercase, lowercase, numbers, and special characters");
|
|
121
|
+
}
|
|
122
|
+
let strength = "weak";
|
|
123
|
+
if (score >= 80) {
|
|
124
|
+
strength = "strong";
|
|
125
|
+
} else if (score >= 60) {
|
|
126
|
+
strength = "medium";
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
isValid: errors.length === 0,
|
|
130
|
+
errors,
|
|
131
|
+
strength,
|
|
132
|
+
score: Math.min(score, 100),
|
|
133
|
+
hints: Array.from(new Set(hints))
|
|
134
|
+
// deduplicate
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
const getPasswordStrengthColor = (strength) => {
|
|
138
|
+
switch (strength) {
|
|
139
|
+
case "weak":
|
|
140
|
+
return "#dc3545";
|
|
141
|
+
case "medium":
|
|
142
|
+
return "#ffc107";
|
|
143
|
+
case "strong":
|
|
144
|
+
return "#28a745";
|
|
145
|
+
default:
|
|
146
|
+
return "#6c757d";
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
const getPasswordStrengthText = (strength) => {
|
|
150
|
+
switch (strength) {
|
|
151
|
+
case "weak":
|
|
152
|
+
return "Weak";
|
|
153
|
+
case "medium":
|
|
154
|
+
return "Medium";
|
|
155
|
+
case "strong":
|
|
156
|
+
return "Strong";
|
|
157
|
+
default:
|
|
158
|
+
return "Unknown";
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export { getPasswordStrengthColor, getPasswordStrengthText, getPasswordValidationOptions, validatePassword };
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{--color-primary:#60a5fa;--color-primary-dark:#3b82f6;--color-primary-light:#93c5fd;--color-secondary:#10b981;--color-secondary-dark:#059669;--color-secondary-light:#34d399;--color-white:#000;--color-black:#fff;--color-gray-50:#111827;--color-gray-100:#1f2937;--color-gray-200:#374151;--color-gray-300:#4b5563;--color-gray-400:#6b7280;--color-gray-500:#9ca3af;--color-gray-600:#d1d5db;--color-gray-700:#e5e7eb;--color-gray-800:#f3f4f6;--color-gray-900:#f9fafb;--color-success:#10b981;--color-success-light:#064e3b;--color-warning:#fbbf24;--color-warning-light:#451a03;--color-warning-text:#fef3c7;--color-error:#ef4444;--color-error-light:#450a0a;--color-error-border:#7f1d1d;--color-error-alt:#f87171;--color-border:#374151;--color-border-light:#4b5563;--color-border-dark:#6b7280;--color-bg-primary:#111827;--color-bg-secondary:#1f2937;--color-bg-tertiary:#374151}@media (prefers-color-scheme:light){.light,:root{--color-primary:#3b82f6;--color-primary-dark:#2563eb;--color-primary-light:#60a5fa;--color-secondary:#059669;--color-secondary-dark:#047857;--color-secondary-light:#10b981;--color-white:#fff;--color-black:#000;--color-gray-50:#f9fafb;--color-gray-100:#f3f4f6;--color-gray-200:#e5e7eb;--color-gray-300:#d1d5db;--color-gray-400:#9ca3af;--color-gray-500:#6b7280;--color-gray-600:#4b5563;--color-gray-700:#374151;--color-gray-800:#1f2937;--color-gray-900:#111827;--color-success:#059669;--color-success-light:#f0fdf4;--color-warning:#f59e0b;--color-warning-light:#fef3c7;--color-warning-text:#92400e;--color-error:#dc2626;--color-error-light:#fef2f2;--color-error-border:#fecaca;--color-error-alt:#db5050;--color-border:#e5e7eb;--color-border-light:#d1d5db;--color-border-dark:#ccc;--color-bg-primary:#fff;--color-bg-secondary:#f9fafb;--color-bg-tertiary:#f3f4f6}}
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
const DEFAULT_OPTIONS = {
|
|
2
|
-
minLength: 8,
|
|
3
|
-
requireUppercase: true,
|
|
4
|
-
requireLowercase: true,
|
|
5
|
-
requireNumbers: true,
|
|
6
|
-
requireSpecialChars: true,
|
|
7
|
-
preventCommonPasswords: true
|
|
8
|
-
};
|
|
9
|
-
const getPasswordValidationOptions = (moduleOptions) => {
|
|
10
|
-
if (!moduleOptions?.passwordValidation) {
|
|
11
|
-
return DEFAULT_OPTIONS;
|
|
12
|
-
}
|
|
13
|
-
return {
|
|
14
|
-
...DEFAULT_OPTIONS,
|
|
15
|
-
...moduleOptions.passwordValidation
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
const COMMON_PASSWORDS = [
|
|
19
|
-
"password",
|
|
20
|
-
"123456",
|
|
21
|
-
"123456789",
|
|
22
|
-
"qwerty",
|
|
23
|
-
"abc123",
|
|
24
|
-
"password123",
|
|
25
|
-
"admin",
|
|
26
|
-
"letmein",
|
|
27
|
-
"welcome",
|
|
28
|
-
"monkey",
|
|
29
|
-
"dragon",
|
|
30
|
-
"master",
|
|
31
|
-
"hello",
|
|
32
|
-
"freedom",
|
|
33
|
-
"whatever",
|
|
34
|
-
"qazwsx",
|
|
35
|
-
"trustno1",
|
|
36
|
-
"jordan",
|
|
37
|
-
"harley",
|
|
38
|
-
"ranger",
|
|
39
|
-
"buster",
|
|
40
|
-
"thomas",
|
|
41
|
-
"tigger",
|
|
42
|
-
"robert",
|
|
43
|
-
"soccer",
|
|
44
|
-
"batman",
|
|
45
|
-
"test",
|
|
46
|
-
"pass",
|
|
47
|
-
"user",
|
|
48
|
-
"guest",
|
|
49
|
-
"login",
|
|
50
|
-
"secret",
|
|
51
|
-
"god",
|
|
52
|
-
"love",
|
|
53
|
-
"sex",
|
|
54
|
-
"money",
|
|
55
|
-
"password1",
|
|
56
|
-
"12345678",
|
|
57
|
-
"qwerty123",
|
|
58
|
-
"admin123",
|
|
59
|
-
"password!",
|
|
60
|
-
"password1!",
|
|
61
|
-
"password123",
|
|
62
|
-
"password123!"
|
|
63
|
-
];
|
|
64
|
-
const validatePassword = (password, options = {}) => {
|
|
65
|
-
const config = { ...DEFAULT_OPTIONS, ...options };
|
|
66
|
-
const errors = [];
|
|
67
|
-
const hints = [];
|
|
68
|
-
let score = 0;
|
|
69
|
-
if (password.length < config.minLength) {
|
|
70
|
-
errors.push(`Password must be at least ${config.minLength} characters long`);
|
|
71
|
-
hints.push(`Use at least ${config.minLength} characters`);
|
|
72
|
-
} else {
|
|
73
|
-
score += 20;
|
|
74
|
-
if (password.length < 12) {
|
|
75
|
-
hints.push("Use 12 or more characters for extra security");
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
if (config.requireUppercase && !/[A-Z]/.test(password)) {
|
|
79
|
-
errors.push("Password must contain at least one uppercase letter");
|
|
80
|
-
hints.push("Add an uppercase letter (A-Z)");
|
|
81
|
-
} else if (/[A-Z]/.test(password)) {
|
|
82
|
-
score += 15;
|
|
83
|
-
}
|
|
84
|
-
if (config.requireLowercase && !/[a-z]/.test(password)) {
|
|
85
|
-
errors.push("Password must contain at least one lowercase letter");
|
|
86
|
-
hints.push("Add a lowercase letter (a-z)");
|
|
87
|
-
} else if (/[a-z]/.test(password)) {
|
|
88
|
-
score += 15;
|
|
89
|
-
}
|
|
90
|
-
if (config.requireNumbers && !/\d/.test(password)) {
|
|
91
|
-
errors.push("Password must contain at least one number");
|
|
92
|
-
hints.push("Add a number (0-9)");
|
|
93
|
-
} else if (/\d/.test(password)) {
|
|
94
|
-
score += 15;
|
|
95
|
-
}
|
|
96
|
-
if (config.requireSpecialChars && !/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password)) {
|
|
97
|
-
errors.push("Password must contain at least one special character");
|
|
98
|
-
hints.push("Add a special character (e.g. !@#$%)");
|
|
99
|
-
} else if (/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password)) {
|
|
100
|
-
score += 15;
|
|
101
|
-
}
|
|
102
|
-
if (config.preventCommonPasswords && COMMON_PASSWORDS.includes(password.toLowerCase())) {
|
|
103
|
-
errors.push("Password is too common. Please choose a more unique password");
|
|
104
|
-
hints.push("Avoid common passwords or names");
|
|
105
|
-
}
|
|
106
|
-
if (password.length >= 12) {
|
|
107
|
-
score += 10;
|
|
108
|
-
} else if (password.length >= 10) {
|
|
109
|
-
score += 5;
|
|
110
|
-
}
|
|
111
|
-
const hasUppercase = /[A-Z]/.test(password);
|
|
112
|
-
const hasLowercase = /[a-z]/.test(password);
|
|
113
|
-
const hasNumbers = /\d/.test(password);
|
|
114
|
-
const hasSpecial = /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password);
|
|
115
|
-
const complexityTypes = [hasUppercase, hasLowercase, hasNumbers, hasSpecial].filter(Boolean).length;
|
|
116
|
-
if (complexityTypes >= 4) {
|
|
117
|
-
score += 10;
|
|
118
|
-
} else if (complexityTypes >= 3) {
|
|
119
|
-
score += 5;
|
|
120
|
-
hints.push("Use a mix of uppercase, lowercase, numbers, and special characters");
|
|
121
|
-
}
|
|
122
|
-
let strength = "weak";
|
|
123
|
-
if (score >= 80) {
|
|
124
|
-
strength = "strong";
|
|
125
|
-
} else if (score >= 60) {
|
|
126
|
-
strength = "medium";
|
|
127
|
-
}
|
|
128
|
-
return {
|
|
129
|
-
isValid: errors.length === 0,
|
|
130
|
-
errors,
|
|
131
|
-
strength,
|
|
132
|
-
score: Math.min(score, 100),
|
|
133
|
-
hints: Array.from(new Set(hints))
|
|
134
|
-
// deduplicate
|
|
135
|
-
};
|
|
136
|
-
};
|
|
137
|
-
const getPasswordStrengthColor = (strength) => {
|
|
138
|
-
switch (strength) {
|
|
139
|
-
case "weak":
|
|
140
|
-
return "#dc3545";
|
|
141
|
-
case "medium":
|
|
142
|
-
return "#ffc107";
|
|
143
|
-
case "strong":
|
|
144
|
-
return "#28a745";
|
|
145
|
-
default:
|
|
146
|
-
return "#6c757d";
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
const getPasswordStrengthText = (strength) => {
|
|
150
|
-
switch (strength) {
|
|
151
|
-
case "weak":
|
|
152
|
-
return "Weak";
|
|
153
|
-
case "medium":
|
|
154
|
-
return "Medium";
|
|
155
|
-
case "strong":
|
|
156
|
-
return "Strong";
|
|
157
|
-
default:
|
|
158
|
-
return "Unknown";
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
export { getPasswordStrengthColor as a, getPasswordStrengthText as b, getPasswordValidationOptions as g, validatePassword as v };
|
|
@@ -16,13 +16,13 @@ type __VLS_Slots = {} & {
|
|
|
16
16
|
default?: (props: typeof __VLS_1) => any;
|
|
17
17
|
};
|
|
18
18
|
declare const __VLS_component: import("vue").DefineComponent<LogoutLinkProps, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
19
|
-
success: () => any;
|
|
20
|
-
error: (error: string) => any;
|
|
21
19
|
click: () => any;
|
|
20
|
+
error: (error: string) => any;
|
|
21
|
+
success: () => any;
|
|
22
22
|
}, string, import("vue").PublicProps, Readonly<LogoutLinkProps> & Readonly<{
|
|
23
|
-
onSuccess?: (() => any) | undefined;
|
|
24
|
-
onError?: ((error: string) => any) | undefined;
|
|
25
23
|
onClick?: (() => any) | undefined;
|
|
24
|
+
onError?: ((error: string) => any) | undefined;
|
|
25
|
+
onSuccess?: (() => any) | undefined;
|
|
26
26
|
}>, {
|
|
27
27
|
redirectTo: string;
|
|
28
28
|
linkText: string;
|
|
File without changes
|
|
File without changes
|