Skip to content
This repository has been archived by the owner on Jun 27, 2020. It is now read-only.

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
dchandekstark committed Apr 21, 2015
1 parent a6bbcd0 commit ad23e8a
Show file tree
Hide file tree
Showing 12 changed files with 182 additions and 64 deletions.
4 changes: 2 additions & 2 deletions lib/ddr/auth/role_based_access_controls_enforcement.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module RoleBasedAccessControlsEnforcement

# List of PIDs for policies on which any of the current user's principals has a policy role
def role_policies
filters = current_user.agents.map { |agent| "policy_role_sim:\"#{agent}\"" }.join(" OR ")
filters = current_user.agents.map { |agent| "#{Ddr::IndexFields::POLICY_ROLE}:\"#{agent}\"" }.join(" OR ")
query = "#{Ddr::IndexFields::ACTIVE_FEDORA_MODEL}:Collection AND (#{filters})"
results = ActiveFedora::SolrService.query(query, rows: Collection.count, fl: "id")
results.map { |r| r["id"] }
Expand All @@ -22,7 +22,7 @@ def policy_role_filters
end

def resource_role_filters
current_user.agents.map { |agent| "resource_role_sim:\"#{agent}\"" }.join(" OR ")
current_user.agents.map { |agent| "#{Ddr::IndexFields::RESOURCE_ROLE}:\"#{agent}\"" }.join(" OR ")
end

def gated_discovery_filters
Expand Down
24 changes: 24 additions & 0 deletions lib/ddr/auth/roles/role_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ module Roles
#
class RoleSet < SimpleDelegator

def self.deserialize(serialized, fmt = :ruby)
if fmt == :json
deserialize JSON.parse(serialized)
else # :ruby
role_set = serialized.map do |role_data|
Role.build(role_data.with_indifferent_access)
end
new(role_set)
end
end

# Grants roles - i.e., adds them to the role set
# Note that we reject roles that are included because
# ActiveTriples::Term#<< does not support isomorphism.
Expand Down Expand Up @@ -61,6 +72,19 @@ def to_a
map.to_a
end

def to_json
serialize(:json)
end

def serialize(fmt = :ruby)
case fmt
when :json
serialize(:ruby).to_json
else # :ruby
to_a.map(&:to_h)
end
end

def where(criteria)
query.where(criteria)
end
Expand Down
3 changes: 3 additions & 0 deletions lib/ddr/index_fields.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ def self.solr_name(*args)
ActiveFedora::SolrService.solr_name(*args)
end

ACCESS_ROLE = solr_name :access_role, :stored_sortable
ACTIVE_FEDORA_MODEL = solr_name :active_fedora_model, :stored_sortable
COLLECTION_URI = solr_name :collection_uri, :symbol
CONTENT_CONTROL_GROUP = solr_name :content_control_group, :searchable, type: :string
Expand Down Expand Up @@ -43,6 +44,8 @@ def self.solr_name(*args)
ORDER = solr_name :struct_metadata__order, :stored_sortable, type: :integer
PERMANENT_ID = solr_name :permanent_id, :stored_sortable, type: :string
PERMANENT_URL = solr_name :permanent_url, :stored_sortable, type: :string
POLICY_ROLE = solr_name :policy_role, :facetable, type: :string
RESOURCE_ROLE = solr_name :resource_role, :facetable, type: :string
TITLE = solr_name :title, :stored_sortable
WORKFLOW_STATE = solr_name :workflow_state, :stored_sortable

Expand Down
2 changes: 2 additions & 0 deletions lib/ddr/managers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ module Managers
extend ActiveSupport::Autoload

autoload :Manager
autoload :AbstractRoleManager
autoload :DerivativesManager
autoload :PermanentIdManager
autoload :RoleManager
autoload :SolrDocumentRoleManager
autoload :WorkflowManager

end
Expand Down
48 changes: 48 additions & 0 deletions lib/ddr/managers/abstract_role_manager.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
module Ddr
module Managers
class AbstractRoleManager < Manager

delegate :granted?, :where, to: :granted

def granted
raise NotImplementedError, "Subclasses must implement `granted'."
end

# Return a list of the permissions granted in scope to any of the agents
def permissions_in_scope_for_agents(scope, agents)
where(scope: scope, agent: agents).map(&:permissions).flatten.uniq
end

# Return a list of the permissions granted in resource scope to any of the agents
def resource_permissions_for_agents(agents)
permissions_in_scope_for_agents("resource", agents)
end

# Return a list of the permissions granted in policy scope to any of the agents
def policy_permissions_for_agents(agents)
permissions_in_scope_for_agents("policy", agents)
end

# Return the permissions granted to the user in resource scope (via roles on the object)
def resource_permissions_for_user(user)
resource_permissions_for_agents(user.agents)
end

# Return the permissions granted to the user in policy scope (via roles on the object)
def policy_permissions_for_user(user)
policy_permissions_for_agents(user.agents)
end

# Return the permissions granted to the user on the object in resource scope, plus
# the permissions granted to the user on the object's admin policy in policy scope
def role_based_permissions(user)
perms = resource_permissions_for_user(user)
if policy = object.admin_policy
perms |= policy.roles.policy_permissions_for_user(user)
end
perms
end

end
end
end
67 changes: 11 additions & 56 deletions lib/ddr/managers/role_manager.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
module Ddr
module Managers
class RoleManager < Manager
class RoleManager < AbstractRoleManager

delegate :grant, :revoke, :granted?, :replace, :revoke_all, :where, to: :granted
delegate :downloader, to: :ds
delegate :grant, :revoke, :replace, :revoke_all, to: :granted
delegate :access_role, :downloader, to: :data_source

def granted
@granted ||= Ddr::Auth::Roles::RoleSet.new(ds.access_role)
@granted ||= Ddr::Auth::Roles::RoleSet.new(access_role)
end

# Revoke all roles in policy scope
Expand All @@ -19,65 +19,20 @@ def revoke_resource_roles
revoke *(where(scope: "resource"))
end

# Return a list of the permissions granted in scope to any of the agents
def permissions_in_scope_for_agents(scope, agents)
where(scope: scope, agent: agents).map(&:permissions).flatten.uniq
end

# Return a list of the permissions granted in resource scope to any of the agents
def resource_permissions_for_agents(agents)
permissions_in_scope_for_agents("resource", agents)
end

# Return a list of the permissions granted in policy scope to any of the agents
def policy_permissions_for_agents(agents)
permissions_in_scope_for_agents("policy", agents)
end

# Return the permissions granted to the user in resource scope (via roles on the object)
def resource_permissions_for_user(user)
resource_permissions_for_agents(user.agents)
end

# Return the permissions granted to the user in policy scope (via roles on the object)
def policy_permissions_for_user(user)
policy_permissions_for_agents(user.agents)
end

# Return the permissions granted to the user on the object in resource scope, plus
# the permissions granted to the user on the object's admin policy in policy scope
def role_based_permissions(user)
perms = resource_permissions_for_user(user)
if policy = object.admin_policy
perms |= policy.roles.policy_permissions_for_user(user)
end
perms
end

# Return a hash of role information to index
# @return [Hash] the fields
def index_fields
granted.each_with_object({}) do |role, fields|
scope_field = scope_index_field(role)
fields[scope_field] ||= []
fields[scope_field] |= [role.agent.first]
scope_role_field = scope_role_index_field(role)
fields[scope_role_field] ||= []
fields[scope_role_field] << role.agent.first
fields = {Ddr::IndexFields::ACCESS_ROLE => granted.serialize(:json)}
granted.each_with_object(fields) do |role, f|
scope_field = "#{role.scope.first}_role_sim"
f[scope_field] ||= []
f[scope_field] |= [role.agent.first]
end
end

private

def scope_index_field(role)
"#{role.scope.first}_role_sim"
end

def scope_role_index_field(role)
"#{role.scope.first}_#{role.role_type.first.underscore}_role_ssim"
end
private

def ds
def data_source
object.adminMetadata
end

Expand Down
11 changes: 11 additions & 0 deletions lib/ddr/managers/solr_document_role_manager.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Ddr
module Managers
class SolrDocumentRoleManager < AbstractRoleManager

def granted
@granted ||= Ddr::Auth::Roles::RoleSet.deserialize(object.access_role, :json)
end

end
end
end
16 changes: 16 additions & 0 deletions lib/ddr/models/solr_document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module SolrDocument

included do
alias_method :pid, :id
delegate :role_based_permissions, to: :roles
end

def to_partial_path
Expand Down Expand Up @@ -78,6 +79,13 @@ def admin_policy_pid
uri &&= ActiveFedora::Base.pid_from_uri(uri)
end

def admin_policy
if admin_policy_pid
query = ActiveFedora::SolrService.construct_query_for_pids([admin_policy_pid])
self.class.new(ActiveFedora::SolrService.query(query))
end
end

def has_children?
ActiveFedora::SolrService.class_from_solr_document(self).reflect_on_association(:children).present?
end
Expand Down Expand Up @@ -189,6 +197,14 @@ def multires_image_file_path
get(Ddr::IndexFields::MULTIRES_IMAGE_FILE_PATH)
end

def roles
@roles ||= Ddr::Managers::SolrDocumentRoleManager.new(self)
end

def access_role
get("access_role_ssi")
end

private

def targets_query
Expand Down
13 changes: 13 additions & 0 deletions spec/auth/roles/role_set_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,19 @@ class ResourceWithRoles < ActiveTriples::Resource
end
end

describe "serialization / deserialization" do
let(:role1) { {type: "Editor", agent: "bob@example.com", scope: "resource"} }
let(:role2) { {type: "Curator", agent: "sue@example.com", scope: "policy"} }
before { subject.grant role1, role2 }
it "should serialize to :ruby format by default" do
expect(subject.serialize).to eq([role1, role2])
end
it "should deserialize to an equalivalent role set" do
expect(described_class.deserialize(subject.serialize)).to eq(subject)
expect(described_class.deserialize(subject.serialize(:json), :json)).to eq(subject)
end
end

describe "#where" do
it "should be a Query object" do
expect(subject.where(type: "Contributor")).to be_a(Query)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
allow(subject).to receive(:role_policies) { ["test:13", "test:45"] }
end
it "should include clauses for is_governed_by relationships to the #role_policies PIDs" do
expect(subject.policy_role_filters).to eq("_query_:\"{!raw f=is_governed_by_ssim}test:13\" OR _query_:\"{!raw f=is_governed_by_ssim}test:45\"")
expect(subject.policy_role_filters).to eq("_query_:\"{!raw f=#{Ddr::IndexFields::IS_GOVERNED_BY}}test:13\" OR _query_:\"{!raw f=#{Ddr::IndexFields::IS_GOVERNED_BY}}test:45\"")
end
end

Expand Down
13 changes: 8 additions & 5 deletions spec/managers/role_manager_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ module Ddr::Managers
expect(obj.adminMetadata).to receive(:access_role) { [] }
subject.granted
end
it "should be a RoleSet" do
expect(subject.granted).to be_a(Ddr::Auth::Roles::RoleSet)
end
end

describe "#index_fields" do
Expand All @@ -32,11 +35,11 @@ module Ddr::Managers
end
before { subject.grant *roles }
it "should return the index fields" do
expect(subject.index_fields).to eq({"resource_curator_role_ssim" => ["bob@example.com"],
"policy_curator_role_ssim" => ["sue@example.com"],
"policy_editor_role_ssim" => ["Editors", "jane@example.com"],
"policy_role_sim" => ["sue@example.com", "Editors", "jane@example.com"],
"resource_role_sim" => ["bob@example.com"]})
expect(subject.index_fields)
.to eq({ Ddr::IndexFields::ACCESS_ROLE => "[{\"type\":\"Curator\",\"scope\":\"resource\",\"agent\":\"bob@example.com\"},{\"type\":\"Curator\",\"scope\":\"policy\",\"agent\":\"sue@example.com\"},{\"type\":\"Editor\",\"scope\":\"policy\",\"agent\":\"Editors\"},{\"type\":\"Editor\",\"scope\":\"policy\",\"agent\":\"jane@example.com\"}]",
Ddr::IndexFields::POLICY_ROLE => ["sue@example.com", "Editors", "jane@example.com"],
Ddr::IndexFields::RESOURCE_ROLE => ["bob@example.com"]
})
end
end

Expand Down
43 changes: 43 additions & 0 deletions spec/models/solr_document_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,47 @@
its(:permanent_id) { is_expected.to eq("foo") }
end

describe "#admin_policy" do
describe "when there is not admin policy relationship" do
its(:admin_policy) { is_expected.to be_nil }
end
describe "where there is an admin policy relationship" do
let!(:query) { ActiveFedora::SolrService.construct_query_for_pids(["test:1"]) }
let!(:admin_policy) { described_class.new({"id"=>"test:1"}) }
before do
subject[Ddr::IndexFields::IS_GOVERNED_BY] = ["info:fedora/test:1"]
allow(ActiveFedora::SolrService).to receive(:query).with(query) { admin_policy }
end
it "should get the admin policy document" do
expect(subject.admin_policy.id).to eq(admin_policy.id)
end
end
end

describe "roles" do
before do
subject[Ddr::IndexFields::ACCESS_ROLE] = "[{\"type\":\"Editor\",\"scope\":\"policy\",\"agent\":\"Editors\"},{\"type\":\"Contributor\",\"scope\":\"resource\",\"agent\":\"bob@example.com\"}]"
end
its(:roles) { is_expected.to be_a(Ddr::Managers::SolrDocumentRoleManager) }
describe "granted roles" do
it "should be a role set" do
expect(subject.roles.granted).to be_a(Ddr::Auth::Roles::RoleSet)
end
it "should deserialize the role data" do
expect(subject.roles.granted).to include(Ddr::Auth::Roles::Role.build(type: "Editor", agent: "Editors", scope: "policy"))
expect(subject.roles.granted).to include(Ddr::Auth::Roles::Role.build(type: "Contributor", agent: "bob@example.com", scope: "resource"))
end
end
describe "permissions" do
let(:admin_policy) { described_class.new({Ddr::IndexFields::ACCESS_ROLE=>"[{\"type\":\"MetadataEditor\",\"scope\":\"policy\",\"agent\":\"bob@example.com\"},{\"type\":\"Viewer\",\"scope\":\"policy\",\"agent\":\"public\"}]"}) }
let(:user) { FactoryGirl.build(:user, username: "bob@example.com") }
before do
allow(subject).to receive(:admin_policy) { admin_policy }
end
it "should calculate the right permissions" do
expect(subject.role_based_permissions(user)).to contain_exactly(:read, :add_children, :download, :edit)
end
end
end

end

0 comments on commit ad23e8a

Please sign in to comment.