@drax/identity-vue 3.13.0 → 3.15.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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "3.13.0",
6
+ "version": "3.15.0",
7
7
  "type": "module",
8
8
  "main": "./src/index.ts",
9
9
  "module": "./src/index.ts",
@@ -27,10 +27,10 @@
27
27
  "@drax/common-front": "^3.11.0",
28
28
  "@drax/common-vue": "^3.11.0",
29
29
  "@drax/crud-front": "^3.11.0",
30
- "@drax/crud-share": "^3.13.0",
31
- "@drax/crud-vue": "^3.13.0",
32
- "@drax/identity-front": "^3.13.0",
33
- "@drax/identity-share": "^3.0.0"
30
+ "@drax/crud-share": "^3.14.0",
31
+ "@drax/crud-vue": "^3.15.0",
32
+ "@drax/identity-front": "^3.15.0",
33
+ "@drax/identity-share": "^3.15.0"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "pinia": "^3.0.4",
@@ -55,5 +55,5 @@
55
55
  "vue-tsc": "^3.2.4",
56
56
  "vuetify": "^3.11.8"
57
57
  },
58
- "gitHead": "5f68eefbcb01c876471e387a815fee1040489c2c"
58
+ "gitHead": "a3bd3419f580b111b26da9e6be2e1cc4c75a056e"
59
59
  }
@@ -38,6 +38,10 @@ export function useAuth() {
38
38
  return await authSystem.changeOwnPassword(currentPassword, newPassword)
39
39
  }
40
40
 
41
+ const passwordPolicy = async () => {
42
+ return await authSystem.passwordPolicy()
43
+ }
44
+
41
45
  const recoveryPasswordRequest = async (email: string) => {
42
46
  return await authSystem.recoveryPasswordRequest(email)
43
47
  }
@@ -99,7 +103,7 @@ export function useAuth() {
99
103
  login, logout, loginWithToken,
100
104
  tokenIsValid, hasPermission,
101
105
  isAuthenticated, fetchAuthUser,
102
- changeOwnPassword, changeAvatar,
106
+ changeOwnPassword, changeAvatar, passwordPolicy,
103
107
  recoveryPasswordRequest, recoveryPasswordComplete,
104
108
  register, switchTenant
105
109
  }
@@ -23,8 +23,8 @@ class UserApiKeyCrud extends EntityCrud implements IEntityCrud {
23
23
  get permissions(){
24
24
  return {
25
25
  manage: 'userApiKey:manage',
26
- view: 'userApiKey:viewMy',
27
- create: 'userApiKey:createMy',
26
+ view: 'userApiKey:view',
27
+ create: 'userApiKey:create',
28
28
  update: 'userApiKey:update',
29
29
  delete: 'userApiKey:delete'
30
30
  }
@@ -0,0 +1,166 @@
1
+ <script setup lang="ts">
2
+ import {computed, onMounted, ref} from "vue";
3
+ import {useAuth} from "../composables/useAuth";
4
+ import type {IPasswordPolicy} from "@drax/identity-front";
5
+
6
+ const {passwordPolicy} = useAuth()
7
+
8
+ const loading = ref(true)
9
+ const errorMsg = ref('')
10
+ const policy = ref<IPasswordPolicy | null>(null)
11
+
12
+ const rules = computed(() => {
13
+ if (!policy.value) {
14
+ return []
15
+ }
16
+
17
+ return [
18
+ {
19
+ label: `Longitud mínima de ${policy.value.minLength} caracteres`,
20
+ enabled: true,
21
+ icon: 'mdi-format-letter-case'
22
+ },
23
+ {
24
+ label: `Longitud máxima de ${policy.value.maxLength} caracteres`,
25
+ enabled: true,
26
+ icon: 'mdi-ruler'
27
+ },
28
+ {
29
+ label: 'Debe incluir al menos una mayúscula',
30
+ enabled: policy.value.requireUppercase,
31
+ icon: 'mdi-format-letter-case-upper'
32
+ },
33
+ {
34
+ label: 'Debe incluir al menos una minúscula',
35
+ enabled: policy.value.requireLowercase,
36
+ icon: 'mdi-format-letter-case-lower'
37
+ },
38
+ {
39
+ label: 'Debe incluir al menos un número',
40
+ enabled: policy.value.requireNumber,
41
+ icon: 'mdi-numeric'
42
+ },
43
+ {
44
+ label: 'Debe incluir al menos un carácter especial',
45
+ enabled: policy.value.requireSpecialChar,
46
+ icon: 'mdi-asterisk'
47
+ },
48
+ {
49
+ label: 'No se permiten espacios',
50
+ enabled: policy.value.disallowSpaces,
51
+ icon: 'mdi-keyboard-space'
52
+ }
53
+ ]
54
+ })
55
+
56
+ const metadata = computed(() => {
57
+ if (!policy.value) {
58
+ return []
59
+ }
60
+
61
+ return [
62
+ {
63
+ label: 'Reutilización',
64
+ value: policy.value.preventReuse > 0
65
+ ? `No se pueden repetir las últimas ${policy.value.preventReuse} contraseñas`
66
+ : 'Sin restricción de reutilización'
67
+ },
68
+ {
69
+ label: 'Expiración',
70
+ value: policy.value.expirationDays
71
+ ? `La contraseña expira cada ${policy.value.expirationDays} días`
72
+ : 'Sin expiración configurada'
73
+ }
74
+ ]
75
+ })
76
+
77
+ async function loadPolicy() {
78
+ try {
79
+ loading.value = true
80
+ errorMsg.value = ''
81
+ policy.value = await passwordPolicy()
82
+ } catch (error) {
83
+ const err = error as Error
84
+ errorMsg.value = err.message || 'No se pudo obtener la política de contraseñas'
85
+ } finally {
86
+ loading.value = false
87
+ }
88
+ }
89
+
90
+ onMounted(loadPolicy)
91
+ </script>
92
+
93
+ <template>
94
+ <v-container fluid class="fill-height">
95
+ <v-row justify="center" align="center">
96
+ <v-col cols="12" md="8" lg="6">
97
+ <v-card variant="elevated">
98
+ <v-card-title class="pa-4">
99
+ Política de contraseñas
100
+ </v-card-title>
101
+
102
+ <v-card-text v-if="loading">
103
+ <div class="text-medium-emphasis">Cargando política...</div>
104
+ </v-card-text>
105
+
106
+ <v-card-text v-else-if="errorMsg">
107
+ <v-alert type="error" variant="tonal">
108
+ {{ errorMsg }}
109
+ </v-alert>
110
+ </v-card-text>
111
+
112
+ <template v-else-if="policy">
113
+ <v-card-text class="pb-2">
114
+ Requisitos que debe cumplir una contraseña válida.
115
+ </v-card-text>
116
+
117
+ <v-card-text class="pt-0">
118
+ <v-list lines="two">
119
+ <v-list-item
120
+ v-for="rule in rules"
121
+ :key="rule.label"
122
+ :prepend-icon="rule.icon"
123
+ >
124
+ <v-list-item-title>{{ rule.label }}</v-list-item-title>
125
+ <template #append>
126
+ <v-chip
127
+ :color="rule.enabled ? 'success' : 'default'"
128
+ size="small"
129
+ variant="tonal"
130
+ >
131
+ {{ rule.enabled ? 'Requerido' : 'Opcional' }}
132
+ </v-chip>
133
+ </template>
134
+ </v-list-item>
135
+ </v-list>
136
+ </v-card-text>
137
+
138
+ <v-divider />
139
+
140
+ <v-card-text>
141
+ <v-row>
142
+ <v-col
143
+ v-for="item in metadata"
144
+ :key="item.label"
145
+ cols="12"
146
+ sm="6"
147
+ >
148
+ <v-sheet class="info-box pa-4 fill-height" rounded>
149
+ <div class="text-caption text-medium-emphasis">{{ item.label }}</div>
150
+ <div class="text-body-1">{{ item.value }}</div>
151
+ </v-sheet>
152
+ </v-col>
153
+ </v-row>
154
+ </v-card-text>
155
+ </template>
156
+ </v-card>
157
+ </v-col>
158
+ </v-row>
159
+ </v-container>
160
+ </template>
161
+
162
+ <style scoped>
163
+ .info-box {
164
+ border: 1px solid rgba(var(--v-theme-on-surface), 0.12);
165
+ }
166
+ </style>
@@ -1,6 +1,7 @@
1
1
  import LoginPage from '../pages/LoginPage.vue'
2
2
  import ProfilePage from '../pages/ProfilePage.vue'
3
3
  import PasswordPage from '../pages/PasswordChangePage.vue'
4
+ import PasswordPolicyPage from '../pages/PasswordPolicyPage.vue'
4
5
  import RegistrationPage from '../pages/RegistrationPage.vue'
5
6
  import PasswordRecoveryRequestPage from '../pages/PasswordRecoveryRequestPage.vue'
6
7
  import PasswordRecoveryCompletePage from '../pages/PasswordRecoveryCompletePage.vue'
@@ -38,6 +39,14 @@ const routes = [
38
39
  auth: false,
39
40
  }
40
41
  },
42
+ {
43
+ name: 'PasswordPolicy',
44
+ path: '/password-policy',
45
+ component: PasswordPolicyPage,
46
+ meta: {
47
+ auth: false,
48
+ }
49
+ },
41
50
  {
42
51
  name: 'PasswordRecoveryRequest',
43
52
  path: '/password/recovery/request',
@@ -1,5 +1,5 @@
1
1
  // Utilities
2
- import { defineStore } from 'pinia'
2
+ import {defineStore} from 'pinia'
3
3
  import UserCrud from "../cruds/user-crud/UserCrud";
4
4
  import UserApiKeyCrud from "../cruds/user-api-key-crud/UserApiKeyCrud";
5
5
  import RoleCrud from "../cruds/role-crud/RoleCrud";
@@ -7,30 +7,30 @@ import TenantCrud from "../cruds/tenant-crud/TenantCrud";
7
7
  import type {IEntityCrud} from "@drax/crud-share";
8
8
 
9
9
  export const useIdentityCrudStore = defineStore('IdentityCrudStore', {
10
- state: (): {
11
- userCrud: IEntityCrud;
12
- userApiKeyCrud: IEntityCrud;
13
- roleCrud: IEntityCrud;
14
- tenantCrud: IEntityCrud;
15
- } => ({
16
- userCrud: UserCrud.instance,
17
- userApiKeyCrud: UserApiKeyCrud.instance,
18
- roleCrud: RoleCrud.instance,
19
- tenantCrud: TenantCrud.instance,
20
- }),
21
- actions:{
22
- setUserCrud(userCrud: IEntityCrud){
23
- this.userCrud = userCrud
24
- },
25
- setUserApiKeyCrud(userApiKeyCrud: IEntityCrud){
26
- this.userApiKeyCrud = userApiKeyCrud
27
- },
28
- setRoleCrud(roleCrud:IEntityCrud){
29
- this.roleCrud = roleCrud
30
- },
31
- setTenantCrud(tenantCrud:IEntityCrud){
32
- this.tenantCrud = tenantCrud
33
- },
10
+ state: (): {
11
+ userCrud: IEntityCrud;
12
+ userApiKeyCrud: IEntityCrud;
13
+ roleCrud: IEntityCrud;
14
+ tenantCrud: IEntityCrud;
15
+ } => ({
16
+ userCrud: UserCrud.instance,
17
+ userApiKeyCrud: UserApiKeyCrud.instance,
18
+ roleCrud: RoleCrud.instance,
19
+ tenantCrud: TenantCrud.instance,
20
+ }),
21
+ actions: {
22
+ setUserCrud(userCrud: IEntityCrud) {
23
+ this.userCrud = userCrud
24
+ },
25
+ setUserApiKeyCrud(userApiKeyCrud: IEntityCrud) {
26
+ this.userApiKeyCrud = userApiKeyCrud
27
+ },
28
+ setRoleCrud(roleCrud: IEntityCrud) {
29
+ this.roleCrud = roleCrud
30
+ },
31
+ setTenantCrud(tenantCrud: IEntityCrud) {
32
+ this.tenantCrud = tenantCrud
33
+ },
34
34
 
35
- }
35
+ }
36
36
  })