solid/authorization-panel

proposal: generalise acp:access

Opened this issue · 6 comments

Having determined that acp:access is the inverse of wac:accessTo in issue 129: acp/wac diff the ACR document, I end up with the mapping between wac and acp

wac:accessTo owl:inverseOf [ 
        owl:propertyChainAxiom ( wac:accessControl :authorizes )
      ] .

being equivalent to

acp:access owl:propertyChainAxiom ( wac:accessControl :authorizes )

The idea was that we could infer the inverse of acp:accessTo from following a wac:accessControl Link and an :authorizes link. Looking at this from the point of view of acp:access suggests that an ACR is its own ACR as the statement

<> acp:access <#p1> .

then would be equivalent to

<> wac:accessControl <> . 
<> :authorizes <#p1> .

But we notice now that we can no longer distinguish Policies that apply to the ldp:Resource linking to the ACR and those that apply to the ACR itself.

For, let us imagine that every ACR has a Link: rel="acp" relation to itself (ie <> acp:accessControl <>) Then the same acp:Policys would apply to the ACR as to the Resource that it is meant to protect.
So for example in this picture we have Tim Berners-Lee's foaf profile that links to the ACR. But now there would be no way - with only these tools - to make the ACR readable only to Tim, without also limiting access to his foaf profile... In this case it seems like it's ok, but there will be other use cases where it won't be quite what is needed.

Illustration of acp Link Relation for TimBL profile

So it seems that we actually can't get rid of the wac:accessTo relation, and that furthermore ACP does not do it. It just replaces it with the inverse acp:access.

But then we can now write out what should appear in Tim's profile using just acp:access were he to want to be the only one to read it.

# this applies to the ACR itself as per current ACP doc dumps
<> acp:access <#TimsPolicy> .

<#TimsPolicy> a acp:Policy;
            acp:allow acp:Read, acp:Write;
            acp:anyOf <#TimRules>  .

# This applies to TimBL's profile 
<card> acp:access [ a acp:Policy;
             acp:allow acp:Read;
             acp:anyOf foaf:Agent ];
       acp:access <#TimsPolicy> .

<#TimBLRules>  acp:agent </People/Berners-Lee/card#i> .

So why not define acp:access to just relate a Resource to the Policy that applies to it?
Like this:

acp:access a rdf:Property ;
    rdfs:label "access"@en ;
    rdfs:comment "The access property identifies the access policies that apply to a resource."@en ;
    rdfs:range acp:Policy ;
    rdfs:domain gen:InformationResource;
    rdfs:isDefinedBy acp: .

Ie we could then have an ACR that looks like this:

Improved ACR

In the above it looks like essentially I bypass the need for the acp:AccessControl class, defined as:

acp:AccessControl a rdfs:Class ;
    rdfs:comment "Access Control statements associate an AccessControlResource with specific Policy definitions."@en .

The only relations that have acp:AccessControl as (co)domain are all the acp:apply* relations which I found to be confusing. This I think explains the confusion: it seems that there is no serious distinction between an acp:AccessControl and a acp:Policy.
Looking at the Access Enforcement doc I found the following pseudo-code that gives a hint as to why the distinction is there. Essentially the pseudo code looks for all the acp:AccessControl statements in the ACR and uses those to evaluate access.

export function isAccessAllowed(agent: Agent, resource: Resource, method: HTTPMethod): boolean {
  const acr: ACR = resource.getAccessControlResource();
  const accessModes: AccessModes = new AccessModes();

  acr.getAccessControls().forEach((ac: AccessControl) => {
    accessModes.add(evaluatePolicies(ac.apply, agent, resource));
    accessModes.add(evaluatePolicies(ac.applyProtected, agent, resource));
    accessModes.add(evaluatePolicies(ac.applyLocked, agent, resource));
  });

  const requiredMode: string = HTTPMethodToAccessMode.get(method);
  if (accessModes.contains(requiredMode)) return true;

  return false;
}

So the type AccessControl is really used to locate the relevant Policies.
The advantages over searching for the resource name as in my proposal above are:

  1. there may be many content-negotiated resources with different extensions .ttl, .rdf, .jsld, etc that have an accessControl Link relation, and that would require the ACR to keep track of different resources. Eg. a server uprade could suddenly add all kinds of mime types to a server for videos.
  2. It removes a brittleness on the client and server since both just need to start from the AccessControl node type in that document for their search.

The disadvantage is in making this distinction between AccessControl type and Policy type, where the AccessControl really just forwards to the Policy. And also the distinction between Policies about the ACR and Policies that apply to another Resource.

Instead of introducing a new type one could also introduce a new relation that would start off from the document, which should be given, as the client or server need to know in which document these relations appear.

<> acp:access <personal#TimsPolicy> .
<> acp:authorizes </const#PublicRead> .

So here we would say that, as above, acp:access only applies the policy to the subject,
whereas acp:authorizes applies the policy to linked-to resources (which do not self-link I guess).

It could be that we get rid of one unnecessary class here, and an apply relation. So this would give us the following:

Bringing acp:authorize back

Lots of comments there to reply to :-)
Perhaps I'll start with a couple of high level statements and then I'll try to go through the content in detail and respond to each.

acp:access is used specifically to control access to the ACR whereas acp:apply is used to control access to the resource associated with the ACR. Both refer to policies. Both could refer to the same policies if that was intended. It's more likely that in a lot of cases the restrictions around access to the ACR would be tighter than those around access to the associated resource.

acp:AccessControl simply groups the policies that control access to the resource. In an earlier version of the documentation, individual acp:AccessControl could be protected separately by policies. We removed this is an unnecessary level of complexity. However we left the acp:AccessControl as a way for applications to manage their own access controls. This allows them to set the minimum access required for particular use cases without having to examine other existing access controls .

Looking at this from the point of view of acp:access suggests that an ACR is its own ACR

Well except an ACR doesn't have an ACR. There is a 1-1 relationship between a normal resource and an ACR. But an ACR is not a normal resource in that its lifecycle is managed by the server. Only some of the statements in an ACR can apply to the ACR itself. Both an ACR and a normal resource are protected by policies.

But we notice now that we can no longer distinguish Policies that apply to the ldp:Resource linking to the ACR and those that apply to the ACR itself.

This depends where we start from. If we start from the ACR then we can distinguish between them based on the predicate used i.e.g acp:accessXXX or acp:applyXXX.

If we start from the policy then this is correct. Policies are reusable so a given policy could be used to protect any number of resources and/or ACRs.

So for example in this picture we have Tim Berners-Lee's foaf profile that links to the ACR. But now there would be no way - with only these tools - to make the ACR readable only to Tim, without also limiting access to his foaf profile... In this case it seems like it's ok, but there will be other use cases where it won't be quite what is needed.

Not sure I understand this.
The profile would be protected by the policies specified using acp:applyXXX while the ACR for the profile would be protected by the policies specified using acp:accessXXX . So different policies can be used to protect the resource and the ACR for the resource. But perhaps I'm misunderstanding the point?

Thanks for the feedback @emmettownsend.

I'll move aside the question about whether ACRs should have ACRs to issue 151 as that is a topic by itself, and is confusing things here. As a result the title of this issue is also no longer correct. The question is really now of defining acp:access a bit more precisely and noticing its relation to acl:accessTo.

(I should perhaps open a new issue and close this one)

What we notice is that we have two ways of tying a resource to a policy.

  1. via the Link: ... rel="acp"... relation and then finding all the "AccessControl" statements in the document.
  2. via the acp:access relation, but only when it applies to the ACR itself

In 2. the acp:access plays very much the same role as acl:accessTo. It tells us to which resource the Policy applies to, but restricted to ACRs. So we have precisely (I think).

acp:access rdfs:subPropertyOf [ owl:inverseOf acl:accessTo ] . 
acp:access rdfs:domain acp:AccessControlResource .

The acp:AccessControl type is there to allow one to know which statements apply to a resource followed by an acp:accessControl relation. Thinking in terms of linked data this led me to propose theacp:authorizes relation from the ACR to the Policy (as shown in the pictures). Then one can follow the link header, arrive on the graph with a pointer on the node identifying the ACR itself, and continue by following the acp:authorizes relation to arrive at the Policy.

That allows us to relate ACL and ACP like this I think:

[   owl:propertyChainAxiom ( acp:accessControl acp:authorizes ) ]  rdfs:subPropertyOf acl:accessTo .

The subPropertyOf in both examples means the inference only goes from left to right (avoiding the issue of ACRs of ACRs).