Verificare permisiuni stergere entitate

Salut

Lucrand singur de aproape 2 ani, ma vad nevoit sa vin cu intrebari tampite pe forum, pentru a ma asigura ca nu o iau pe aratura :grin: povestea e destul de simpla:

Am un api cu un endpoint de delete dashboard, de tip DELETE in care trimit parametrul in url ( apiUrl/dashboards/delete?id=123). Nimic complicat, din controller ajung in serviciu, unde mai intai sterg dependintele, si apoi dashboardul.

controller:

def delete(id: Int) = Action.async { request =>
   service.delete(id)
}

service:

def delete(id: Int) = {
   val deps = for {
      f1 <- repo.deleteWidgets(id) // returneaza true
      f2 <- repo.deleteUsers(id) // returneaza true
   } yield (f1, f2)

   deps map { results => {
        results match {
           case (true, true) => repo.delete(id)
           case _ => throw new Exception("Dependintele nu pot fi sterse")
        }
     }
  }
}

Pana aici sper ca fluxul este ok, altul nu stiu :joy: Problema apare dupa o noua dezvoltare. Un user (care este singurul admin al unui dashboard) poate sa sharuiasca altuia cu posibilitatea de editare/stergere widgeturi, dar fara a putea sterge dashboardul. Asa am ajuns la ideea ca trebuie sa trimit si userId-ul celui conectat, din frontend, si endpointul meu va avea parametrii ?id=123&userId=7, doar ca nu mi-a placut ideea de a trimite 2 parametrii pentru delete si nici nu cred ca am vazut pe undeva practica asta. Mi-am adus rapid aminte ca folosesc Bearer Token si ca pot salva userId’ul acolo si asa il pot scoate direct din request in backend.

astfel controllerul devine:

def delete(id: Int) = Action.async { request =>
   val claims = authService.decodeToken(request)
   service.delete(id, claims.userId)
}

iar serviciul:

def delete(id: Int, userId: Int) = {
  // am o tabela de legatura DashboardUser cu campurile dashboardId, userId, role
  repo.getByIds(id, userId) map {
    (du: DashboardUser) => du.role match {
       case "admin" => // tot codul serviciului de mai sus
       case _ => throw new Exception("Nu ai permisiunea de a sterge acest dashboard")
    }
  }
 
   val deps = for {
      f1 <- repo.deleteWidgets(id)
      f2 <- repo.deleteUsers(id)
   } yield (f1, f2)

   deps map { results => {
        results match {
           case (true, true) => repo.delete(id)
           case _ => throw new Exception("Can not delete dependencies")
        }
     }
  }
}

Ca de obicei am lungit postarile prea mult :joy: incerc sa fac un rezumat:

  1. in controller, scot userId-ul celui care face actiunea, din token
  2. verific daca userul respectiv are rolul “admin” pentru a putea sterge entitatea
  3. sterg dependintele
  4. sterg entitatea

Stiu ca astea putea fi evitata din frontend, ascunzand butonul de “delete” userilor care nu au rolul de “admin”, dar imi place sa lucrez cu dubla validare si incerc sa nu las “portite” in backend :crazy_face:

Si intrebarea … este o abordare corecta tot ce-am scris mai sus? Exista si alte variante? merci

Nu cred ca are relevanta, dar limbajul este Scala (Play framework)

Salutare!

Ce ai facut tu este un middleware. E o bucata de cod care valideaza requestul, iar de-abia apoi trimite requestul mai departe.

Request → middleware → controller

Ce imi place mie sa fac cand fac designul unui API este sa tin totul cat se poate de CRUD posibil, chiar si la nivel de rute/metode. Asta iti face si viata mai usoara cand adaugi roluri/permisiuni.

De exemplu, ce as face eu ar fi sa modific ruta astfel:

DELETE /dashboards/{dashboardId}

Apoi in middleware poti pur si simplu sa verifici daca acel token are access la entitatile dashboard, apoi daca are permisiunile necesare sa stearga acel dashboard, etc.

Un avantaj al acestei abordari este ca poti simplu sa refolosesti acelasi midleware pentru mai multe rute simultan.

GET /dashboards/{dashboardId}
PUT /dashboards/{dashboardId}

Deasemenea eviti cu metoda asta sa ai logica de validare permisiuni in controller. Practic tot codul legat de asta este scos din controller si controllerul nu mai trebuie sa stie nimic de partea de roluri/permisiuni.

Sper sa te ajute. Mult success!

2 Likes