feat : creation d'une page d'aministration pour la ajout / modification d'utlisateur
This commit is contained in:
125
frontend/components/user/user-form.vue
Normal file
125
frontend/components/user/user-form.vue
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
<template>
|
||||||
|
<form @submit.prevent="validate">
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-between gap-10">
|
||||||
|
<h1 class="text-3xl font-bold uppercase">
|
||||||
|
{{ userId ? "Modifications de l'utilisateur" : "Ajout d'un utilisateur" }}
|
||||||
|
</h1>
|
||||||
|
<button
|
||||||
|
class="text-xl uppercase bg-primary-500 text-white h-[50px] w-[272px]"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
{{ userId ? 'Sauvegarder' : 'Ajouter' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid gap-y-16 gap-x-40 mb-16">
|
||||||
|
<UiTextInput
|
||||||
|
id="user-name"
|
||||||
|
v-model="form.username"
|
||||||
|
label="Nom de l'utilisateur"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UiSelect
|
||||||
|
id="user-role"
|
||||||
|
v-model="form.role"
|
||||||
|
label="Rôle de l'utilisateur"
|
||||||
|
:options="[
|
||||||
|
{ value: ROLE.ROLE_USER, label: 'User' },
|
||||||
|
{ value: ROLE.ROLE_ADMIN, label: 'Admin' },
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
<UiTextInput
|
||||||
|
id="user-password"
|
||||||
|
v-model="form.password"
|
||||||
|
label="Mot de passe"
|
||||||
|
type="password"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import {computed, reactive, ref, watch} from 'vue'
|
||||||
|
import {ROLE} from '~/utils/constants'
|
||||||
|
import {createUser, updateUser, getUser} from '~/services/auth'
|
||||||
|
import type {UserData} from '~/services/dto/user-data'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const userId = computed(() => resolveUserId(route.params.id))
|
||||||
|
const isLoading = ref(false)
|
||||||
|
const isHydrating = ref(false)
|
||||||
|
|
||||||
|
const resolveUserId = (param: unknown) => {
|
||||||
|
const idStr = Array.isArray(param) ? param[0] : param
|
||||||
|
if (!idStr) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const id = Number(idStr)
|
||||||
|
return Number.isFinite(id) ? id : null
|
||||||
|
}
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
username: '',
|
||||||
|
role: '',
|
||||||
|
password: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const hydrateFromUser = (user: UserData | null) => {
|
||||||
|
if (!user) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isHydrating.value = true
|
||||||
|
form.username = user.username ?? ''
|
||||||
|
const roles = user.roles ?? []
|
||||||
|
const hasAdmin = roles.includes(ROLE.ROLE_ADMIN)
|
||||||
|
form.role = hasAdmin ? ROLE.ROLE_ADMIN : ROLE.ROLE_USER
|
||||||
|
form.password = ''
|
||||||
|
isHydrating.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => userId.value,
|
||||||
|
async (id) => {
|
||||||
|
if (id === null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isLoading.value = true
|
||||||
|
try {
|
||||||
|
const user = await getUser(id)
|
||||||
|
hydrateFromUser(user)
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{immediate: true}
|
||||||
|
)
|
||||||
|
|
||||||
|
async function validate() {
|
||||||
|
|
||||||
|
const normalizedUsername = form.username.trim()
|
||||||
|
const normalizedRole = form.role.trim()
|
||||||
|
const normalizedPassword = form.password.trim()
|
||||||
|
|
||||||
|
const basePayload = {
|
||||||
|
username: normalizedUsername,
|
||||||
|
roles: normalizedRole ? [normalizedRole] : undefined,
|
||||||
|
password: normalizedPassword || undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userId.value) {
|
||||||
|
await updateUser(userId.value, basePayload)
|
||||||
|
await router.push(`/admin/user/list/`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const created = await createUser(basePayload)
|
||||||
|
if (created) {
|
||||||
|
await router.push(`/admin/user/list/`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -57,7 +57,9 @@
|
|||||||
"auth": {
|
"auth": {
|
||||||
"login": "Identifiants invalides.",
|
"login": "Identifiants invalides.",
|
||||||
"users": "Impossible de récupérer les utilisateurs.",
|
"users": "Impossible de récupérer les utilisateurs.",
|
||||||
"logout": "Impossible de se déconnecter."
|
"logout": "Impossible de se déconnecter.",
|
||||||
|
"update": "Impossible de mettre à jour l'utilisateur.",
|
||||||
|
"create": "Impossible de créer l'utilisateur."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"success": {
|
"success": {
|
||||||
@@ -65,6 +67,8 @@
|
|||||||
"update": "Réception mise à jour avec succès."
|
"update": "Réception mise à jour avec succès."
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
|
"update": "Utilisateur mis à jour avec succès.",
|
||||||
|
"create": "Utilisateur créé avec succès.",
|
||||||
"login": "Connexion réussie.",
|
"login": "Connexion réussie.",
|
||||||
"logout": "Déconnexion réussie."
|
"logout": "Déconnexion réussie."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
<!-- Liste des liens à ajouter ci-dessous -->
|
<!-- Liste des liens à ajouter ci-dessous -->
|
||||||
<!--Button pour afficher le component admin-users -->
|
<!--Button pour afficher le component admin-users -->
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
to="/admin/user-list"
|
to="/admin/user/list"
|
||||||
class="block px-4 py-2 rounded hover:bg-primary-600 transition"
|
class="block px-4 py-2 rounded hover:bg-primary-600 transition"
|
||||||
>
|
>
|
||||||
Utilisateurs
|
Utilisateurs
|
||||||
@@ -58,8 +58,9 @@
|
|||||||
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import auth from "~/layouts/auth.vue";
|
import {useAuthStore} from '~/stores/auth'
|
||||||
|
|
||||||
|
const auth = useAuthStore()
|
||||||
const { version } = useAppVersion()
|
const { version } = useAppVersion()
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -20,7 +20,10 @@
|
|||||||
Accueil
|
Accueil
|
||||||
</a>
|
</a>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<NuxtLink to="/admin/dashboard" custom v-slot="{ href, navigate, isActive }">
|
<NuxtLink
|
||||||
|
to="/admin/dashboard" custom v-slot="{ href, navigate, isActive }"
|
||||||
|
v-if="auth.isAdmin"
|
||||||
|
>
|
||||||
<a
|
<a
|
||||||
:href="href"
|
:href="href"
|
||||||
@click="navigate"
|
@click="navigate"
|
||||||
@@ -107,27 +110,27 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAuthStore } from '~/stores/auth'
|
import {useAuthStore} from '~/stores/auth'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
const isMenuOpen = ref(false)
|
const isMenuOpen = ref(false)
|
||||||
const { version } = useAppVersion()
|
const {version} = useAppVersion()
|
||||||
|
|
||||||
const closeMenu = () => {
|
const closeMenu = () => {
|
||||||
isMenuOpen.value = false
|
isMenuOpen.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleMenu = () => {
|
const toggleMenu = () => {
|
||||||
isMenuOpen.value = !isMenuOpen.value
|
isMenuOpen.value = !isMenuOpen.value
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
try {
|
try {
|
||||||
await auth.logout()
|
await auth.logout()
|
||||||
} finally {
|
} finally {
|
||||||
closeMenu()
|
closeMenu()
|
||||||
await navigateTo('/login')
|
await navigateTo('/login')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<admin-users v-if="activeCode === 'users'" />
|
<AdminUserForm/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: 'admin'
|
layout: 'admin'
|
||||||
})
|
})
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
const activeCode = computed(() => (route.query.code as string))
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
8
frontend/pages/admin/user/[[id]].vue
Normal file
8
frontend/pages/admin/user/[[id]].vue
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<template>
|
||||||
|
<UserForm/>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
definePageMeta({
|
||||||
|
layout: 'admin'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex items-center justify-between gap-10">
|
<div class="flex items-center justify-between">
|
||||||
<h1 class="text-3xl font-bold uppercase">Utilisateurs</h1>
|
<h1 class="text-3xl font-bold uppercase">Liste des utilisateurs</h1>
|
||||||
<button
|
<NuxtLink
|
||||||
class="text-xl uppercase bg-primary-500 text-white h-[50px] w-[272px]"
|
class="flex items-center justify-center text-xl uppercase bg-primary-500 text-white h-[50px] w-[272px]"
|
||||||
@click="null"
|
@click="router.push('/admin/user/')"
|
||||||
>
|
>
|
||||||
Ajouter
|
Ajouter
|
||||||
</button>
|
</NuxtLink>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -14,39 +15,27 @@
|
|||||||
<div class="grid grid-cols-3 gap-4 bg-slate-100 px-4 py-3 text-sm font-semibold uppercase tracking-wide">
|
<div class="grid grid-cols-3 gap-4 bg-slate-100 px-4 py-3 text-sm font-semibold uppercase tracking-wide">
|
||||||
<div>Username</div>
|
<div>Username</div>
|
||||||
<div>Role</div>
|
<div>Role</div>
|
||||||
<div>Action</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-for="user in userList"
|
v-for="user in userList"
|
||||||
:key="user.id"
|
:key="user.id"
|
||||||
class="grid grid-cols-3 gap-4 px-4 py-3 text-sm hover:bg-slate-50 cursor-pointer border-t border-slate-200 items-center"
|
class="grid grid-cols-3 gap-4 px-4 py-3 text-sm hover:bg-slate-50 cursor-pointer border-t items-center"
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
|
@click="goToUser(user.id)"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
{{ user.username}}
|
{{ user.username }}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{{ user.roles?.join(', ') || ' ---' }}
|
{{ user.roles?.join(', ') || ' ---' }}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<div class="p-4">
|
|
||||||
<button
|
|
||||||
class="text-xl uppercase bg-primary-500 text-white h-[50px] w-[272px]"
|
|
||||||
@click="null"
|
|
||||||
>
|
|
||||||
Modifier
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: 'admin'
|
layout: 'admin'
|
||||||
@@ -56,7 +45,11 @@ import type {UserData} from "~/services/dto/user-data";
|
|||||||
import {getUsers} from "~/services/auth";
|
import {getUsers} from "~/services/auth";
|
||||||
|
|
||||||
const userList = ref<UserData[]>([])
|
const userList = ref<UserData[]>([])
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const goToUser = (id: number) => {
|
||||||
|
router.push(`/admin/user/${id}`)
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
userList.value = await getUsers()
|
userList.value = await getUsers()
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useApi } from '~/composables/useApi'
|
import { useApi } from '~/composables/useApi'
|
||||||
import type { UserData } from '~/services/dto/user-data'
|
import type { UserData } from '~/services/dto/user-data'
|
||||||
|
import type {UserPayload} from "~/services/dto/user-data";
|
||||||
|
|
||||||
export async function getUsers() {
|
export async function getUsers() {
|
||||||
const api = useApi()
|
const api = useApi()
|
||||||
@@ -13,6 +14,28 @@ export async function getUsers() {
|
|||||||
return data['hydra:member'] ?? []
|
return data['hydra:member'] ?? []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getUser(id: number) {
|
||||||
|
const api = useApi()
|
||||||
|
return api.get<UserData>(`users/${id}`, {}, {
|
||||||
|
toastErrorKey: 'errors.auth.user'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createUser(payload: UserPayload = {}) {
|
||||||
|
const api = useApi()
|
||||||
|
return api.post<UserData>('users', payload, {
|
||||||
|
toastErrorKey: 'errors.auth.create',
|
||||||
|
toastSuccessKey : 'success.auth.create'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateUser(id : number, playload: UserPayload = {}){
|
||||||
|
const api = useApi()
|
||||||
|
return api.patch<UserData>(`users/${id}`, playload, {
|
||||||
|
toastErrorKey: 'errors.auth.update',
|
||||||
|
toastSuccessKey: 'success.auth.update'
|
||||||
|
})
|
||||||
|
}
|
||||||
export async function getCurrentUser() {
|
export async function getCurrentUser() {
|
||||||
const api = useApi()
|
const api = useApi()
|
||||||
return api.get<UserData>('me', {}, {
|
return api.get<UserData>('me', {}, {
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
export interface UserData {
|
export interface UserData {
|
||||||
id: number
|
id: number
|
||||||
username: string
|
username: string
|
||||||
|
roles: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserPayload = {
|
||||||
|
username?: string
|
||||||
|
password?: string
|
||||||
roles?: string[]
|
roles?: string[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,63 +1,80 @@
|
|||||||
import { defineStore } from 'pinia'
|
import {defineStore} from 'pinia'
|
||||||
import type { UserData } from '~/services/dto/user-data'
|
import type {UserData} from '~/services/dto/user-data'
|
||||||
import { getCurrentUser, login, logout } from '~/services/auth'
|
import {getCurrentUser, createUser, login, logout} from '~/services/auth'
|
||||||
|
import type {UserPayload} from "~/services/dto/user-data";
|
||||||
|
import {ROLE} from '~/utils/constants'
|
||||||
|
|
||||||
export const useAuthStore = defineStore('auth', {
|
export const useAuthStore = defineStore('auth', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
user: null as UserData | null,
|
user: null as UserData | null,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
checked: false
|
checked: false
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
isAuthenticated: (state) => Boolean(state.user)
|
isAuthenticated: (state) => Boolean(state.user),
|
||||||
},
|
isAdmin: (state) => Boolean(state.user?.roles?.includes(ROLE.ROLE_ADMIN))
|
||||||
actions: {
|
|
||||||
clearSession() {
|
|
||||||
this.user = null
|
|
||||||
this.checked = true
|
|
||||||
this.isLoading = false
|
|
||||||
},
|
},
|
||||||
async ensureSession() {
|
actions: {
|
||||||
if (this.checked) {
|
clearSession() {
|
||||||
return this.user
|
this.user = null
|
||||||
}
|
this.checked = true
|
||||||
|
this.isLoading = false
|
||||||
|
},
|
||||||
|
async ensureSession() {
|
||||||
|
if (this.checked) {
|
||||||
|
return this.user
|
||||||
|
}
|
||||||
|
|
||||||
this.checked = true
|
this.checked = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const me = await getCurrentUser()
|
const me = await getCurrentUser()
|
||||||
this.user = me
|
this.user = me
|
||||||
return me
|
return me
|
||||||
} catch {
|
} catch {
|
||||||
this.user = null
|
this.user = null
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async login(username: string, password: string) {
|
async login(username: string, password: string) {
|
||||||
this.isLoading = true
|
this.isLoading = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await login(username, password)
|
await login(username, password)
|
||||||
const me = await getCurrentUser()
|
const me = await getCurrentUser()
|
||||||
this.user = me
|
this.user = me
|
||||||
this.checked = true
|
this.checked = true
|
||||||
return me
|
return me
|
||||||
} finally {
|
} finally {
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async logout() {
|
async createUser(payload: UserPayload = {}) {
|
||||||
this.isLoading = true
|
this.isLoading = true
|
||||||
|
const result = await createUser(payload).finally(() => {
|
||||||
|
this.isLoading = false
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
},
|
||||||
|
async updateUser(id: number, payload: UserPayload) {
|
||||||
|
this.isLoading = true
|
||||||
|
const result = await createUser(payload).finally(() => {
|
||||||
|
this.isLoading = false
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
},
|
||||||
|
async logout() {
|
||||||
|
this.isLoading = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await logout()
|
await logout()
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore logout errors so we can still clear local auth state.
|
// Ignore logout errors so we can still clear local auth state.
|
||||||
} finally {
|
} finally {
|
||||||
this.user = null
|
this.user = null
|
||||||
this.checked = true
|
this.checked = true
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ export const MERCHANDISE_TYPE_CODES = {
|
|||||||
AUTRES: 'AUTRES'
|
AUTRES: 'AUTRES'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
export const ROLE = {
|
||||||
|
ROLE_ADMIN : 'ROLE_ADMIN',
|
||||||
|
ROLE_USER : 'ROLE_USER'
|
||||||
|
}
|
||||||
export const SUPLLIER_CODE = {
|
export const SUPLLIER_CODE = {
|
||||||
LIOT: 'LIOT'
|
LIOT: 'LIOT'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ namespace App\Entity;
|
|||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
use ApiPlatform\Metadata\Get;
|
use ApiPlatform\Metadata\Get;
|
||||||
use ApiPlatform\Metadata\GetCollection;
|
use ApiPlatform\Metadata\GetCollection;
|
||||||
|
use ApiPlatform\Metadata\Patch;
|
||||||
|
use ApiPlatform\Metadata\Post;
|
||||||
use App\State\MeProvider;
|
use App\State\MeProvider;
|
||||||
|
use App\State\UserPasswordProcessor;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
@@ -28,10 +31,27 @@ use Symfony\Component\Serializer\Attribute\Groups;
|
|||||||
normalizationContext: ['groups' => ['user:read']],
|
normalizationContext: ['groups' => ['user:read']],
|
||||||
security: "is_granted('ROLE_USER')"
|
security: "is_granted('ROLE_USER')"
|
||||||
),
|
),
|
||||||
new GetCollection(
|
new Post(
|
||||||
normalizationContext: ['groups' => ['user:read']],
|
normalizationContext: ['groups' => ['user:read']],
|
||||||
|
denormalizationContext: ['groups' => ['user:write']],
|
||||||
|
security: "is_granted('ROLE_ADMIN')",
|
||||||
|
processor: UserPasswordProcessor::class
|
||||||
|
),
|
||||||
|
new Patch(
|
||||||
|
normalizationContext: ['groups' => ['user:read']],
|
||||||
|
denormalizationContext: ['groups' => ['user:write']],
|
||||||
|
security: "is_granted('ROLE_ADMIN')",
|
||||||
|
processor: UserPasswordProcessor::class
|
||||||
|
),
|
||||||
|
new GetCollection(
|
||||||
|
normalizationContext: ['groups' => ['user-login:read']],
|
||||||
security: "is_granted('PUBLIC_ACCESS')"
|
security: "is_granted('PUBLIC_ACCESS')"
|
||||||
),
|
),
|
||||||
|
new GetCollection(
|
||||||
|
uriTemplate: '/admin/users',
|
||||||
|
normalizationContext: ['groups' => ['user:read']],
|
||||||
|
security: "is_granted('ROLE_ADMIN')"
|
||||||
|
),
|
||||||
],
|
],
|
||||||
normalizationContext: ['groups' => ['user:read']],
|
normalizationContext: ['groups' => ['user:read']],
|
||||||
paginationEnabled: false
|
paginationEnabled: false
|
||||||
@@ -41,18 +61,19 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
|||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\GeneratedValue]
|
#[ORM\GeneratedValue]
|
||||||
#[ORM\Column(type: 'integer')]
|
#[ORM\Column(type: 'integer')]
|
||||||
#[Groups(['user:read', 'reception:read'])]
|
#[Groups(['user:read', 'user-login:read', 'reception:read'])]
|
||||||
private ?int $id = null;
|
private ?int $id = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 180, unique: true)]
|
#[ORM\Column(length: 180, unique: true)]
|
||||||
#[Groups(['user:read', 'reception:read'])]
|
#[Groups(['user:read', 'user:write', 'user-login:read', 'reception:read'])]
|
||||||
private string $username = '';
|
private string $username = '';
|
||||||
|
|
||||||
#[ORM\Column(type: 'json')]
|
#[ORM\Column(type: 'json')]
|
||||||
#[Groups(['user:read'])]
|
#[Groups(['user:write', 'user:read'])]
|
||||||
private array $roles = [];
|
private array $roles = [];
|
||||||
|
|
||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
|
#[Groups(['user:write'])]
|
||||||
private string $password = '';
|
private string $password = '';
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
|
|||||||
40
src/State/UserPasswordProcessor.php
Normal file
40
src/State/UserPasswordProcessor.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\State;
|
||||||
|
|
||||||
|
use ApiPlatform\Metadata\Operation;
|
||||||
|
use ApiPlatform\State\ProcessorInterface;
|
||||||
|
use App\Entity\User;
|
||||||
|
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||||
|
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||||
|
|
||||||
|
final class UserPasswordProcessor implements ProcessorInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly UserPasswordHasherInterface $hasher,
|
||||||
|
#[Autowire(service: 'api_platform.doctrine.orm.state.persist_processor')]
|
||||||
|
private readonly ProcessorInterface $persistProcessor
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
|
||||||
|
{
|
||||||
|
if ($data instanceof User) {
|
||||||
|
$plain = $data->getPassword();
|
||||||
|
if ('' !== $plain) {
|
||||||
|
$data->setPassword($this->hasher->hashPassword(
|
||||||
|
$data,
|
||||||
|
$plain
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->persistProcessor->process(
|
||||||
|
$data,
|
||||||
|
$operation,
|
||||||
|
$uriVariables,
|
||||||
|
$context
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user