No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Aprende todo un fin de semana sin pagar una suscripción 🔥

Aprende todo un fin de semana sin pagar una suscripción 🔥

Regístrate

Comienza en:

5D
22H
0M
48S

Roles y permisos

14/30
Recursos

Aportes 19

Preguntas 0

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

o inicia sesión.

¡Wohoo! Soy el admin 😎 jajaja. Para el caso del final yo crearía roles. Asignarle roles a cada usuario (ya no solo un campo de user.isAdmin, sino un campo user.role) y con base en ese campo validar cada acción para saber si su rol le permite o no hacer X acción :p

Roles y permisos

La autorización es la que le permite a los usuarios tener cierto tipo de roles que nos permiten saber a que permisos tenemos acceso en nuestra aplicación, actualizar información o borrarla, tener permisos solo de lectura de la información, poder subir información etc .

Podemos tener distintos tipos de roles, por mencionar algunos pueden ser el administrador, moderadores, usuarios premiun o freemiun y demás.

Para empezar a implementar esta lógica de autorización vamos a nuestro archivo auth.js:

// Listas de Autorización
const adminList = [
  'RetaxMaster',
  'freddier',
  'juandc'
];

const AuthContext = React.createContext();

function AuthProvider({ children }) {
  /* Dentro de nuestra función de login veremos si el usuario a 
  ingresar es administrador */
  const login = ({ username }) => {
    /* Esta es la validación para saber si el usuario que ingresa a 
		la aplicación es administrador o no */
    const isAdmin = adminList.find(admin => admin === username);
    /* Ahora nuestros usuarios van a tener una propiedad para saber 
		si son admis */
    setUser({ username, isAdmin });
    navigate('/profile');
  }
}

Ahora en nuestra aplicación vamos a crear los permisos que van a tener nuestros administradores.

BlogPost.js

import { useAuth } from '../../auth/auth';

function BlogPost() {
	...	

  // Vamos a hacer uso de la autenticación
  const auth = useAuth();

	/* Aqui buscamos al momento de registrarnos si el usuario es parte 
  de la lista de administradores */
  const blogpost = blogdata.find( post => post.slug == slug );

  return (
    <>
      ...
      {/* Si nuestro usuario existe y es admin vamos a renderizar 
      este botón */}
      {auth.user?.isAdmin && (
        <button>Eliminar blogpost</button>
      )} 
    </>
  );
}

Ahora si entramos a alguno de los BlogPost con el nombre de algún administrador vamos a poder ver renderizado nuestro botón de eliminar BloPost.

Ya tenemos la lógica para mostrar contenido dependiendo del rol.

Ahora si vemos los blogpost que tienen autores, digamos que el autor del blog desea eliminarlo, pero ¿como lo va a hacer?

Vamos a crear la lógica para que los autores del contenido puedan eliminar sus propios Blogs.

function BlogPost() {
  ...

  /* Aquí validaremos si al acceder al blog soy el autor del blog y 
	pueda eliminarlo o tenga el rol de administrador para ello */
  const canDelete = auth.user?.isAdmin || blogpost.author === auth.user?.username;

  const returnToBlog = () => {
    navigate('/blog');
  }
  
  return (
    <>
      ...

      {/* Ahora vamos a preguntar si puedo borrar el BlogPost*/}
      {canDelete && (
        <button>Eliminar blogpost</button>
      )} 
    </>
  );
}

Ahora en caso de que seamos lo autores del Blog podemos eliminarlo, super fácil.

Pense que iba a ser complicado pero me resulto sencillo, cambiaria algunos nombres de los objetos.
Todavia tengo dudas de como se crean las API y los objetos JSON, si tienen una regla de planificar y organizar

const roles = {
    admin: {
        type: 'admin',
        read: true,
        write: true,
        delete: true
    },
    editor: {
        type: 'editor',
        read: true,
        write: true,
        delete: true
    },
    student: {
        type: 'student',
        write: false,
        read: true,
        delete: false
    },
}

const users = [{
    name: 'ivana',
    rol: roles.admin
},
{
    name: 'fred',
    rol: roles.admin
},
{
    name: 'cris',
    rol: roles.admin
},
{
    name: 'rocio',
    rol: roles.student
},
{
    name: 'leonel',
    rol: roles.editor
}]

const login = ({username}) => {
        const admin = users.filter(admin => admin.rol.type === 'admin')
        const isAdmin = admin.find(admin => admin.name === username)  . .. . . . .  }



//blogspot

    const canDelete = auth.user?.isAdmin || blogPost.author === auth.user?.username;

    // const filterA = auth.user?.admin?.filter( f => f.name && f.rol.delete)

    // console.log(filterA.find(f => auth.user?.username === f.name )) //devuelve un objeto con nombres de admin especificos

Reto

Mi solución al reto fue crear un arreglo de roles, con permisos anidados:

auth.js

const roles = [
  { role: "admin", update: true, delete: true },
  { role: "creator", update: true, delete: true },
  { role: "editor", update: true, delete: false },
];

Luego, crear una función para validad que el permiso que recibimos en la consulta existe:

function checkRoles(role) {
  return roles.find((item) => item.role === role);
}

Luego, crear un método que modifique el objecto user que se envia al provider:

function addPermissions(user) {
  if (!checkRoles(user.role)) return { ...user, update: false, delete: false };
  const role = roles.filter((item) => item.role === user.role);

  const newUser = { ...user, ...role[0] };
  return newUser;
}

Al crear hacer login quedaría así:

const login = ({ username }) => {

    let user = {
      username,
      isAdmin: false,
      role: "editor",
    };

    user = addPermissions(user);

    setUser(user);
    navigate("/profile");
  };

En componente de BlogPost solo debe de determinar que tipo de permisos tiene el usuario:
BlogPost.js

	const auth = useAuth();
	...
	{auth.user?.update && <button>Actualizar blogpost</button>}
	{auth.user?.delete && <button>Delete blogpost</button>}

Otra forma de saber si el usuario es admin:

const adminList = ["Irisval", "RetaxMaster", "freddier"]

const isAdmin = adminList.includes(username)

MDN - Array.prototype.includes

Les dejo un blogpost muy valioso donde explican un poco el tema de las rutas protegidas de una forma escalable. Espero les sirva como a mi 😉
https://www.robinwieruch.de/react-router-private-routes

Leyendo varios aportes mi solución fue de la siguiente manera:

en Auth.js crear una constante para los roles y sus permisos respectivos;
y despues un array de los usuarios registrados

const roles = {
  admin: {
    write: true,
    read: true,
    delete: true,
  },
  editor: {
    write: true,
    read: true,
    delete: false,
  },
  visitor: {
    write: false,
    read: true,
    delete: false,
  },
};

const users = [
  {
    name: "Andres",
    role: roles.admin,
  },
  {
    name: "Felipe",
    role: roles.editor,
  },
];

Dentro de AuthProvider asignar el usuario y su rol

function AuthProvider({ children }) {
//............
  const login = (username) => {
    //revisar si el usuario existe o lo crea como visitante
    const rol = users.find((usu) => usu.name === username);
    rol !== undefined
      ? setUser(rol)
      : setUser({ name: username, role: roles.visitor });
    navigate("/profile");
  };
 //............
}

finalmente en BlogPost.jsx hacer las validaciones para renderizar un boton o no
dependiendo de los permisos de su rol

return (
    <>
      <h2>{blog.title}</h2>
      <button onClick={handleBack}>Back</button>
      <p>{blog.author}</p>
      <p>{blog.content}</p>
      {user?.role.write && <button>Edit Post</button>}
      {user?.role.delete && <button>Delete Post</button>}
      {user?.name === blog.author && (
        <>
          <button>Edit Post</button>
          <button>Delete Post</button>
        </>
      )}
    </>
  );

Apoyándome en los comentarios de los compañeros y lo aprendido en el curso, cree un array, que incluye a las personas autorizadas, los roles y permisos

const userType = [
    {
        name: ['Irisval', 'RetaxMaster', 'freddier'],
        role: 'admin',
        permissions: {
            create: true,
            read: true,
            update:false,
            delete: true,
        }
    },
    {
        name: ['yahe', 'pedro', 'pablo'],
        role: 'author',
        permissions: {
            create: true,
            read: true,
            update:true,
            delete: true,
        },

    },
];

Luego lo recorro para obtener las características del usuario(esto en la función login):

 let role = []
    let permissions = {}
    let userRole = ''
    const isAuthorized = userType.some(user => user.name.includes(username))

    userType.forEach((user) => {
      if (user.name.includes(username) && isAuthorized) {
        role.push(user.role)
        userRole = role.join()
        permissions = { ...user.permissions }
      } else if (!user.name.includes(username) && !isAuthorized) {
        role = ['visitor']
        userRole = role.join()
        permissions = {
          write: false,
          read: true,
          update: false,
          delete: false,
        }
      }
    })

setUser({ username, isAuthorized, userRole, permissions });
    navigate('/profile');

En el blog, de acuerdo a los permisos que tiene cada rol, cree una condición para que el administrador y el autor puedan borrar, pero solo el autor de cada blog, pueda editar

const postList = blogData.find(post => post.slug === slug)
   
 const canDelete = (auth.user?.isAuthorized && auth.user?.permissions.delete)&&
     (postList.author === auth.user?.username || auth.user.userRole === 'admin')

    const canUpdate = auth.user?.isAuthorized && auth.user?.permissions.update &&
    postList.author === auth.user?.username

El retorno quedaría así:

return (
        <>
          <h2>{postList.title}</h2>
          <button onClick={returnToBlog}>Volver al blog</button>
          <p>{postList.author}</p>
          <p>{postList.content}</p>
    
          {canDelete && (
            <button>Eliminar blogpost</button>
          )}

          {canUpdate && (
            <button>Editar blogpost</button>
          )}
        </>
      );

Inspirado en unos de los compañeros dejo una simple y humilde solucion.

En el primero array determinaremos los roles y lo que pueden hacer.
En el segundo crearemos usuarios con roles.

const roles = {
    admin: {
        type: 'admin',
        read: true,
        write: true,
        delete: true
    },
    editor: {
        type: 'editor',
        read: true,
        write: true,
        delete: false
    },
}

const users = [{
    name: 'ivana',
    rol: roles.admin
},
{
    name: 'fred',
    rol: roles.admin
},
{
    name: 'leonel',
    rol: roles.editor
}]

En la funcion de autenticacion de Loguin agregaremos una linea extra para extraer el rol.

  const login = ({ username }) => {
        const Isrol = users.find(user => (user.name === username) ? user.rol : null)
        setUser({ username, Isrol })
        navigator('/profile')
    }

y al final, en el blogPost veremos si tienen autorizacion o no.

const authorityDelet = auth.user?.Isrol?.rol.delete
    const authorityEdit = auth.user?.Isrol?.rol.write
    return (
        <>
            <h2> {post.title}</h2>
            <p>{post.content}</p>
            <button onClick={returnToBlog}>Volver un paso atras</button>
            {authorityEdit && (
                <button>Editar blog</button>
            )}
            {authorityDelet && (
                <button>Eliminar blog</button>
            )}
        </>
    )
}

Podria ser algo asi:

{
    admin: {
        create: true,
        view: true
        edit: true,
        delete: true
    },

    writter: {
        create: true,
        view: true,
        edit: true,
        delete: false
    },

    ...
}

Yo simplemente cree más arrays e hice más comprobaciones. Tiene más sentido agregar una propiedad “role” al user, pero ya que es un proyecto de prueba y que la lógica es casi la misma lo dejé así.

import React from 'react'
import { useNavigate, useParams } from 'react-router-dom'
import { blogData } from '../data/blogdata'
import { useAuth } from './auth'

export function BlogPost() {
    const navigate = useNavigate()
    const { slug } = useParams()
    const { user } = useAuth()

    const blogPost = blogData.find(post => post.slug === slug)
    const userIsAuthor = user?.username === blogPost.author
    const userIsEditor = user?.isEditor
    const userIsSpellChecker = user?.isSpellChecker
    const userIsModerator = user?.isModerator

    const returnToBlog = () => {
        navigate('/blog')
    }

    return (
        <>
            <button onClick={returnToBlog}>return to blog</button>
            <h2>{blogPost.title}</h2>
            <li>{blogPost.author}</li>
            <p>{blogPost.content}</p>

            {userIsModerator && <button>mark as best</button>}
            {(userIsModerator || userIsAuthor) && <button>delete blog</button>}
            {(userIsEditor || userIsAuthor) && <button>Modify blog</button>}
            {(userIsSpellChecker || userIsAuthor) && (
                <button>Correct spelling</button>
            )}
        </>
    )
}

Para el reto decidi crear un botón editar el cual solo debe aparecer a usuarios administradores y editores sin embargo los editores no podrán ver el botón eliminar post.

para ello la lista adminList la convertí en un array de objetos

const adminList = [
  { username: 'almendev', role: 'admin' },
  { username: 'juandc', role: 'editor' },
  { username: 'freddier', role: 'admin' }
]; 

ahora los usuarios tiene un rol que identificar al momento de renderizar ciertas funciones, ahora la función login queda de la siguiente manera

const login = ({ username }) => {
    const user = adminList.find(admin => admin.username === username) || { username: username, role: 'user' };
    setUser(user);
    navigate('/profile');
  };

de esta forma si el usuario esta dentro del array de usuarios con permisos especiales se retornada el objeto con su username y role sino se retornara un objeto que contiene los mismo campos sin embargo atribuye un rol por defecto llamado user por poner un nombre.

con esto hecho ya solo queda modificar el componente blogpost,
para lo cual ajuste el canDelete para que valide ahora el atributo role.

const canDelete = auth.user?.role === 'admin' || blogpost.author === auth.user?.username;

y para los editores agregue el siguiente condicional

{auth.user?.role === 'editor' && (
        <button>Editar</button>
      )}

si esta forma de hacerlo no es correcta me gustaría me ayuden con sus comentarios a mejorar.

Gracias

// Crear una lista nueva en auth.js
const editorList = ['Francisco', 'Javier', 'Luis'];
// Hacer la validación de su nombre y agregarlo al actualizador 
const isAdmin = adminList.find(admin => admin === username);
const isEditor = editorList.find(editor => editor === username);
setUser({ username, isAdmin, isEditor });

// Hacer la validación en BlogPost, creando una variable nueva
const canEdit = auth.user?.isEditor || blogpost.author === auth.user?.username;
// Crear un botón nuevo, para editores.
{canEdit && (
        <button>Editar blogpost</button>
      )}

Esta es mi solución al reto de Juan:

separe la lista de personas y lo coloque en un archivo whiteList.js simplemente para tener un poco más de orden

const whiteList = []

whiteList.push({
    name: 'eze',
    role: {
        admin: true,
        editor: true,
        QA: true,
        student: false,
    },
})


whiteList.push({
    name: 'agos',
    role: {
        admin: true,
        editor: true,
        QA: true,
        student: false,
    },
})


whiteList.push({
    name: 'nano',
    role: {
        admin: false,
        editor: true,
        QA: false,
        student: false,
    },
})


whiteList.push({
    name: 'erika',
    role: {
        admin: false,
        editor: true,
        QA: false,
        student: false,
    },
})

whiteList.push({
    name: 'marce',
    role: {
        admin: false,
        editor: false,
        QA: true,
        student: false,
    },
})


whiteList.push({
    name: 'alguien',
    role: {
        admin: false,
        editor: false,
        QA: true,
        student: false,
    },
})

whiteList.push({
    name: 'pepito',
    role: {
        admin: false,
        editor: false,
        QA: true,
        student: false,
    },
})

whiteList.push({
    name: 'otros',
    role: {
        admin: false,
        editor: false,
        QA: false,
        student: true,
    },
})

export { whiteList }

Luego, en nuestro archivo auth.js realice los siguientes cambios:

import { whiteList } from './whiteLIst'

const AuthContext = React.createContext()

function AuthProvider({ children }) {
    const navigate = useNavigate()
    const [user, setUser] = useState(null)


    const roleOf = (username) => {
        const user = whiteList.find(item => username === item.name )
        if (user) return user.role
        return {
            admin: false,
            editor: false,
            QA: false,
            student: true,
        }
    } 
    
    const login = ({ username }) => {
        const autorization = roleOf(username)
        setUser({ username, autorization })
        navigate('/profile')
    }

   ...
}

por último, en el archivo Blogpost.jsx hice las validaciones:

import React from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useAuth } from './auth';
import { blogData } from './blogData'

function BlogPost() {
    const navigate = useNavigate()
    const { slug } = useParams();
    const auth = useAuth()

    const blogpost = blogData.find( post => post.slug === slug );

    const isAdmin = auth.user.autorization.admin

    const isEditor = auth.user.autorization.editor || blogpost.author === auth.user?.username

    const isQA = auth.user.autorization.QA

    const isStudent = auth.user.autorization.student

    const returnToBlog = () => {
        navigate('/blog')
    }
    
    return (
    <>
        <h2>{blogpost.title}</h2>
        <button onClick={returnToBlog} >volver al blog</button>
        <p>{blogpost.author}</p>
        <p>{blogpost.content}</p>

        {(isAdmin || isEditor) && (
            <button>Eliminar blogpost</button>
        )}

        {(isAdmin || isQA) && (
            <button>El blog es correcto</button>
        )}

        {(isStudent) && (
            <button>Me gusta</button>
        )}
    </>
    );
}

export { BlogPost }

Esta fue la solución que se me ocurrió, si se te ocurre algo mejor no dudes en dejarlo en los comentarios 😄

Yo lo que haria es que tendria un useState o un useReducer para cada role y dependiendo del rol se renderice una UI u otra

Para saber si un elemento existe en una lista podemos usar some de esta manera:

const isAdmin = adminList.some(admin => admin === username)

Para el reto podemos hacer asi:

// App.js

<Route path="/" element={<RequireAuth allowedRoles={['member', 'root']} />}>
	<Route index element={<Home />} />
	{/* Mis rutas */}
</Route>

// RequireAuth.js

const RequireAuth = ({ allowedRoles }) => {
  return isAuthenticated ? (
    <Layout>
       {
         allowedRoles.includes(roleUser) ? (
           <Outlet />
         ) : (
           <Navigate to="/unauthorized"  />
         )
        }
    </Layout>
    : (
      <Navigate to="/auth/login"  />
    )
}

export default RequireAuth

Yo era de esos men, que tuvo su propio servidor Habbo Hotel. Este curso me recuerda mucho cuando modificaba y otorgaba los permisos como dueño de ese lugar.

Recuerdo que utilizaba PHP para quitar y poner.

!Ahhh, que recuerdos.!

Me pueden ayudar un poco, cuando me registro no sale el nombre del registrado xd

y también cuando cierro sesión no se quita el loguot :C
https://github.com/SRgenius1/Project-with-react-router/tree/main/components