-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Authentication with Panoptes Roles 💯 (#100)
* Add Pundit, remove dupes * Add Pundit * Roles are pulled out of a JSONB and keys must be strings * Add ApplicationPolicy and spec * Add UserRoles concern * * Call super instead of repeating the gem * Rescue from Pundit::Unauthorized * Don't call verify_authorized until this is done * Update project roles * ProjectPolicy, spec, and AppPolicy updates * ProjectController updates * Workflow policy & controller updates * Authorize singletons on #show routes * Return a 403 on failed auth. Give each policy its own scope. * TranscriptionPolicy & spec, only allow approvers to approve * Add auth verifier after_actions * Don't join a table if you've already got a foreign key Co-Authored-By: Campbell Allen <[email protected]> * Inline projects method, don't needlessly compact * Move PanoptesApi out of app/lib and into app/services * Move UserRole logic out of concern and into RolesChecker service * Skip auth on root status route * Move permissions questions to policy, use new policy action for approval check * Remove comment * Properly memoize project ids. Move empty? check to role checker. * Reduce number of db calls to get transcription project policy * Rename some methods+classes, privatize and un-spec others Co-authored-by: Campbell Allen <[email protected]>
- Loading branch information
Showing
25 changed files
with
736 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
class ApplicationPolicy | ||
attr_reader :user, :records, :role_checker | ||
|
||
def initialize(user, records) | ||
@user = user | ||
@records = Array.wrap(records) | ||
raise Pundit::NotAuthorizedError, "must be logged in to Panoptes" unless logged_in? | ||
@role_checker = ProjectRoleChecker.new(user, @records) | ||
end | ||
|
||
def index? | ||
admin? || (logged_in? && viewer?) | ||
end | ||
|
||
def show? | ||
admin? || (logged_in? && viewer?) | ||
end | ||
|
||
def admin? | ||
logged_in? && user.admin | ||
end | ||
|
||
def logged_in? | ||
!!user | ||
end | ||
|
||
class Scope | ||
attr_reader :user, :scope, :role_checker | ||
|
||
def initialize(user, scope) | ||
raise Pundit::NotAuthorizedError, "must be logged in to Panoptes" unless user | ||
@user = user | ||
@scope = scope | ||
@role_checker = ProjectRoleChecker.new(user, scope) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
class ProjectPolicy < ApplicationPolicy | ||
def editor? | ||
role_checker.can_edit? | ||
end | ||
|
||
def approver? | ||
role_checker.can_approve? | ||
end | ||
|
||
def viewer? | ||
role_checker.can_view? | ||
end | ||
|
||
class Scope < Scope | ||
def resolve | ||
if user.admin? | ||
scope.all | ||
else | ||
viewer_policy_scope | ||
end | ||
end | ||
|
||
def viewer_policy_scope | ||
scope.where id: role_checker.viewer_project_ids | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
class TranscriptionPolicy < ApplicationPolicy | ||
delegate :editor?, :approver?, :viewer?, to: :project_policy | ||
|
||
def update? | ||
has_update_rights? | ||
end | ||
|
||
def approve? | ||
if has_update_rights? | ||
approver? || admin? | ||
else | ||
false | ||
end | ||
end | ||
|
||
def has_update_rights? | ||
admin? || (logged_in? && editor?) | ||
end | ||
|
||
def project_policy | ||
workflow_ids = records.map(&:workflow_id).uniq | ||
ProjectPolicy.new(user, Project.joins(:workflows).where(workflows: { id: workflow_ids }).distinct) | ||
end | ||
|
||
class Scope < Scope | ||
def resolve | ||
viewer_policy_scope | ||
end | ||
|
||
def viewer_policy_scope | ||
if user.admin? | ||
scope.all | ||
elsif user | ||
scope.joins(:workflow).where(workflows: { project_id: role_checker.viewer_project_ids } ) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
class WorkflowPolicy < ApplicationPolicy | ||
delegate :editor?, :approver?, :viewer?, to: :project_policy | ||
|
||
def project_policy | ||
ProjectPolicy.new(user, Project.where(id: records.pluck(:project_id).uniq)) | ||
end | ||
|
||
class Scope < Scope | ||
def resolve | ||
viewer_policy_scope | ||
end | ||
|
||
def viewer_policy_scope | ||
if user.admin? | ||
scope.all | ||
elsif user | ||
scope.joins(:project).where project_id: role_checker.viewer_project_ids | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
class ProjectRoleChecker | ||
attr_reader :user, :records, :viewer_project_ids | ||
|
||
EDITOR_ROLES = %w(owner collaborator expert scientist moderator) | ||
APPROVER_ROLES = %w(owner collaborator) | ||
VIEWER_ROLES = %w(owner collaborator expert scientist tester) | ||
|
||
def initialize(user, records) | ||
@user = user | ||
@records = records | ||
@viewer_project_ids = get_viewer_project_ids | ||
end | ||
|
||
def can_edit? | ||
ids = user_project_ids(user.roles, EDITOR_ROLES) | ||
check_roles(ids, records) | ||
end | ||
|
||
def can_approve? | ||
ids = user_project_ids(user.roles, APPROVER_ROLES) | ||
check_roles(ids, records) | ||
end | ||
|
||
def can_view? | ||
ids = user_project_ids(user.roles, VIEWER_ROLES) | ||
check_roles(ids, records) | ||
end | ||
|
||
def get_viewer_project_ids | ||
user_project_ids(user.roles, VIEWER_ROLES) | ||
end | ||
|
||
private | ||
|
||
def user_project_ids(user_roles, allowed_roles) | ||
allowed_role_ids = [] | ||
user_roles.each do |id, roles| | ||
if (roles & allowed_roles).any? | ||
allowed_role_ids << id | ||
end | ||
end | ||
allowed_role_ids | ||
end | ||
|
||
def check_roles(project_ids, records) | ||
return false if records.empty? | ||
records.all? do |record| | ||
project_ids.include? record.id.to_s | ||
end | ||
end | ||
end |
Oops, something went wrong.