Skip to content

Commit

Permalink
"defaultAccessContainer" as per solid/specification#325 (comment)
Browse files Browse the repository at this point in the history
  • Loading branch information
bblfish committed May 23, 2023
1 parent aeff2a6 commit 2df151d
Show file tree
Hide file tree
Showing 15 changed files with 206 additions and 195 deletions.
12 changes: 2 additions & 10 deletions src/main/scala/run/cosy/Solid.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,11 @@ object Solid:
// todo: replace names with .ac

given system: ActorSystem[Nothing] = ctx.system
val defaultAcl = uri.copy(path = uri.path ?/ ".acl")
val rootRef: ActorRef[AcceptMsg] = ctx.spawn(
BasicContainer(uri.withoutSlash, fpath, Root(defaultAcl)),
BasicContainer(uri.withoutSlash, fpath, Root(uri.withSlash)),
"solid"
)
SolidPostOffice(system).addRoot(uri, rootRef)
SolidPostOffice(system).addRoot(uri.withSlash, rootRef)
// todo: why this and given reg?
val solid = new Solid(uri, fpath)
given timeout: Scheduler = system.scheduler
Expand Down Expand Up @@ -244,13 +243,6 @@ class Solid(
// todo: the path here supposes that the root container is at the root of the web server
// we may want more flexibility here...

// todo: remove this commented code. Just there until commit
// def routeWith(replyTo: ActorRef[HttpResponse]): LDP.Route = LDP.RouteMsg(
// NonEmptyList("/", remaining),
// LDP.CmdMessage(SolidCmd.plain(reqc.request), agent, replyTo),
// acDir
// ).nextRoute(Root(baseUri.copy(path = Uri.Path("/.acl"))))

// todo: do we need the nextRoute(...) added above?
SolidPostOffice(sys).ask[HttpResponse](SolidCmd.plain(reqc.request), agent)
.map(RouteResult.Complete)
15 changes: 9 additions & 6 deletions src/main/scala/run/cosy/http/auth/Guard.scala
Original file line number Diff line number Diff line change
Expand Up @@ -173,16 +173,15 @@ object Guard:
* @param msg
* the message to authorize
* @param msgACL
* the closest default ACL from the message if known
* @param aclUri
* the URL of the acl that allows access
* the closest default ACL from the message if known, or the effective acl
* @param aclUri the direct act for the request resource
*/
def Authorize[T](msg: CmdMessage[T], msgACL: ACInfo, aclUri: Uri)(using
context: ActorContext[ScriptMsg[?] | Do]
): Unit =
// todo!!: set timeout in config!!
given timeOut: akka.util.Timeout = akka.util.Timeout(Duration(3, TimeUnit.SECONDS))
context.log.info(s"Authorize(WannaDo($msg), <$aclUri>)")
context.log.info(s"Authorize(WannaDo($msg), <$msgACL>)")
import SolidCmd.{Get, Wait}
import run.cosy.http.auth.Guard.*
import cats.free.Free.pure
Expand All @@ -192,6 +191,8 @@ object Guard:
msg.commands match
case p: Plain[?] =>
import msg.given
val effectiveAcl = msgACL.aclUri

// todo: this is an optimisation to send the message directly to the ACL actor,
// but it requires getting the acl ActorRef be of a type which accepts ScriptMsg[Boolean]
// containers do that, but we'd need to check if
Expand All @@ -208,12 +209,14 @@ object Guard:
context.self, // todo: see above, should be able to send directly to destination
ref =>
ScriptMsg[Boolean](
authorizeScript(msgACL.acl, msg.from, msg.target, p.req.method),
authorizeScript(effectiveAcl, msg.from, msg.target, p.req.method),
WebServerAgent,
ref
)
) {
case Success(true) => Do(msg)
case Success(true) =>
context.log.info(s"authorized ${msg.target} by user ${msg.from} ")
Do(msg)
case Success(false) =>
context.log.info(s"failed to authorize ${msg.target} ")
msg.respondWithScr(HttpResponse(
Expand Down
13 changes: 9 additions & 4 deletions src/main/scala/run/cosy/http/util/UriX.scala
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ object UriX:
case Path.Segment(head, tail) => Some(head)
case _ => None

/** find the container for this uri */
def container: Uri = uri.withPath(uri.path.container)

/** remove uri without the final slash, or the same */
def withoutSlash: Uri =
val rev: Uri.Path = uri.path.reverse
Expand All @@ -57,10 +60,12 @@ object UriX:

/** add slash to the end the final slash, or the same */
def withSlash: Uri =
uri.withPath(uri.path ++ Uri.Path./)
// rev match
// case Uri.Path.Segment(_) => uri.withPath(Uri.Path.Slash(rev).reverse)
// case _ => uri
// uri.withPath(uri.path ++ Uri.Path./)
val rev = uri.path.reverse
rev match
case Uri.Path.Empty => uri.withPath(Uri.Path./)
case Uri.Path.Segment(name,tail) => uri.withPath(Slash(Uri.Path.Segment(name,tail)).reverse)
case _ => uri

/** replace fileName with Name in Uri or else place filename after slash or add an initial
* slash Todo: improve - this definintion feels very ad-hoc ...
Expand Down
41 changes: 28 additions & 13 deletions src/main/scala/run/cosy/ldp/ACInfo.scala
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
/*
* Copyright 2021 Henry Story
*
* SPDX-License-Identifier: Apache-2.0
*/
/** Copyright 2021 Henry Story
*
* SPDX-License-Identifier: Apache-2.0
*/

package run.cosy.ldp

import akka.actor.ActorPath
import akka.actor.typed.ActorRef
import akka.http.scaladsl.model.Uri
import akka.http.scaladsl.model.Uri.Path
import run.cosy.http.util.UriX.*
import run.cosy.ldp.ACInfo.{ACContainer, ACtrlRef}
import run.cosy.ldp.Messages.{Route, ScriptMsg, WannaDo}
import run.cosy.ldp.fs.Resource.AcceptMsg

Expand All @@ -19,18 +20,35 @@ import scala.annotation.tailrec

/** see [../README](../README.md). Avery ACInfo gives us the acl Uri Path. But we can either know
* the actor of the container or of the AC Resource itself, hence two subtypes
*
* @param container
* the URI of the container in which the ACL is stored. (the container may be a resource, since
* resources can have a number of representations (and so contain those).
*/
enum ACInfo(val acl: Uri):
enum ACInfo(val container: Uri):
// todo: this should not be needed, use ACContainer instead
// mhh, could also be useful for links from acl files to themselves.
// todo: in which case find a better name
case Root(acu: Uri) extends ACInfo(acu)
case Root(containerUrl: Uri) extends ACInfo(containerUrl)
// todo: container type should be ActorRef[Route|ScriptMsg[Boolean]] so that we can send acl scripts directly
/** The ACContainer actor is not the ACL actor, but its parent */
case ACContainer(acu: Uri, container: ActorRef[Route]) extends ACInfo(acu)
case ACContainer(containerUrl: Uri, ref: ActorRef[Route]) extends ACInfo(containerUrl)

/** The ACRef actor is the one containing the ACR graph */
case ACtrlRef(acu: Uri, actor: ActorRef[AcceptMsg]) extends ACInfo(acu)
case ACtrlRef(
forContainer: Uri,
acName: String,
actor: ActorRef[AcceptMsg],
isContainerAcl: Boolean
) extends ACInfo(forContainer)

// todo: different directories may want their own naming conventions for Urls, so we should pass
// the acl name around too. But for now, let's calculate it
def aclUri: Uri = this match
case ACtrlRef(container, acName, _, isContainer) =>
if isContainer then container ?/ acName
else container.sibling(acName + ".acl")
case other => other.container ?/ ".acl"

object ACInfo:
extension (path: Uri.Path)
Expand All @@ -43,10 +61,7 @@ object ACInfo:
rec(path, root)

extension (path: ActorPath)
def toUri: Uri.Path =
path.elements.foldLeft(Uri.Path./)((p, name) => p / name)

def toUri(starting: Int) =
def toUriPath(starting: Int=0): Path =
path.elements.drop(starting).foldLeft(Uri.Path./)((p, name) => p ?/ name)

end ACInfo
12 changes: 5 additions & 7 deletions src/main/scala/run/cosy/ldp/ResourceRegistry.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,15 @@ import akka.actor.typed.*
import akka.actor.typed.scaladsl.Behaviors
import akka.http.scaladsl.model.{HttpRequest, HttpResponse, Uri}
import run.cosy.Solid.pathToList
import run.cosy.http.util.UriX.*
import run.cosy.ldp.fs.BasicContainer

import java.util.concurrent.atomic.AtomicReference
import scala.annotation.tailrec
import run.cosy.ldp.DirTree
import run.cosy.ldp.ACInfo
import run.cosy.ldp.ACInfo.ACContainer

import run.cosy.ldp.Messages.*

/** Whenever an LDPR actor goes up it should register itself here, so that messages can be routed
* directly to the right actor, rather than passing the messages through the container hierarchy.
* See [[ResourceRegistry.md ResourceRegistry]]
Expand All @@ -42,7 +41,6 @@ class ResourceRegistry(rootUri: Uri, rootLDPC: ActorRef[Messages.Route])
extends PathDB[ActorRef[Messages.Route], Boolean]:

override def hasAcl(a: Boolean): Boolean = a
val rootACUri = rootUri.copy(path = rootUri.path ?/ ".acl")

// todo: remove the Option, requires knowing the root ActorRef
/** We map the information in the DB to what is more useable. todo: Later if this works we can
Expand All @@ -54,9 +52,9 @@ class ResourceRegistry(rootUri: Uri, rootLDPC: ActorRef[Messages.Route])
getActorRef(path).map { (lst, containerRef, lastACContainerRefOpt) =>
val ac: ACInfo = lastACContainerRefOpt match
case None => // todo: it would be nice if we could get rid of this
Root(rootACUri)
Root(rootUri.withSlash)
case Some(lastAC) => // todo: replace 2 below with calcualted value
val acr = lastAC.path.toUri(2) ?/ ".acl"
ACContainer(rootUri.copy(path = acr), lastAC)
val acr = lastAC.path.toUriPath(2)
ACContainer(rootUri.copy(path = acr).withSlash, lastAC)
(lst, containerRef, ac)
}.getOrElse((path, rootLDPC, Root(rootACUri)))
}.getOrElse((path, rootLDPC, Root(rootUri.withSlash)))
6 changes: 4 additions & 2 deletions src/main/scala/run/cosy/ldp/SolidPostOffice.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ class SolidPostOffice(system: ActorSystem[?]) extends Extension:
type Ref = ActorRef[Messages.Route]
type Attr = Boolean

/** to start very simple we start with only allowing domain roots. todo: later add data
* structures to have paths higher on a server. todo: also wrap in a more secure structure later
/** to start very simple we start with only allowing domain roots.
* todo: later add data structures to have paths higher on a server.
* todo: also wrap in a more secure structure later
* todo: the map as to be from (protocol, Authority) to resourceRegistry
*/
val roots: AtomicReference[Map[Uri.Authority, ResourceRegistry]] = new AtomicReference(Map())

Expand Down
Loading

0 comments on commit 2df151d

Please sign in to comment.