app/actions/PermissionAction.scala (70 lines of code) (raw):
package actions
import actions.PermissionAction.checkPermission
import com.gu.googleauth.AuthAction
import com.gu.googleauth.AuthAction.UserIdentityRequest
import com.typesafe.scalalogging.LazyLogging
import play.api.mvc.{ActionBuilder, ActionFilter, AnyContent, PlayBodyParsers, Result, Results}
import services.{DynamoPermissionsCache, UserPermissions}
import services.UserPermissions.Permission
import scala.concurrent.{ExecutionContext, Future}
/**
* This class provides separate Actions for read and write endpoints.
* Each will first perform a google auth check, then do the permissions check for the user.
*/
class AuthAndPermissionActions(
authAction: ActionBuilder[AuthAction.UserIdentityRequest, AnyContent],
readPermissionAction: Option[PermissionAction],
writePermissionAction: Option[PermissionAction]
) {
// for endpoints that read data
val read = readPermissionAction match {
case Some(readAction) => authAction andThen readAction
case None => authAction
}
// for endpoints that write data
val write = writePermissionAction match {
case Some(writeAction) => authAction andThen writeAction
case None => authAction
}
}
object AuthAndPermissionActions {
// Does google auth check but no permissions checks
def withoutPermissionsChecks(authAction: ActionBuilder[AuthAction.UserIdentityRequest, AnyContent]) =
new AuthAndPermissionActions(authAction, None, None)
}
object PermissionAction extends Results with LazyLogging {
private def logAndForbid(message: String): Result = {
logger.info(message)
Forbidden(message)
}
// In the filter function we return None if the user has permission to access the resource,
// otherwise we return a 403 Forbidden to the client
def checkPermission(
email: String,
page: String,
requiredPermission: Permission,
userPermissions: Option[List[UserPermissions.PagePermission]]
): Option[Result] = {
userPermissions match {
case Some(permissions) =>
permissions.find(permission => permission.name == page) match {
case Some(userPermission) =>
userPermission.permission match {
case Permission.Write => None // user has full access
case Permission.Read =>
if (requiredPermission != Permission.Write) None
else Some(logAndForbid(s"Invalid permission for user ${email}, for page $page"))
}
case None => Some(logAndForbid(s"No permission found for user ${email}, for page $page"))
}
case None => Some(logAndForbid(s"No permissions found for user ${email}"))
}
}
}
class PermissionAction(
page: String,
requiredPermission: Permission,
permissionsService: DynamoPermissionsCache,
val parse: PlayBodyParsers,
val executionContext: ExecutionContext,
) extends ActionFilter[UserIdentityRequest] {
override protected def filter[A](request: UserIdentityRequest[A]): Future[Option[Result]] = Future.successful {
checkPermission(
request.user.email,
page,
requiredPermission,
permissionsService.getPermissionsForUser(request.user.email)
)
}
}