Question
· Dec 11, 2024

Multiple %CSP.REST.AccessCheck() Overrides

I have a primary dispatch class that implements %CSP.REST.AccessCheck() for some high level access checks before the route table forwards the request to the implementation classes which also implement %CSP.REST.AccessCheck() for some lower level access checks.  The idea is that we know where we are in the implementation class and what the user is trying to do, so it makes more sense to check some lower level items there rather than trying to parse out the request at the dispatch class.

The problem is the second, implementation level access check is not happening.  My first thought was since I allow access via the higher level dispatch class access check, that the access "carried through", however, looking at %CSP.REST.Page() I see no reason ..AccessCheck() should not fire again.

Method signature: ClassMethod AccessCheck(Output pAuthorized As %Boolean = 0) As %Status

Should I be able to override and implement %CSP.REST.AccessCheck() on every class that extends %CSP.REST?

Product version: IRIS 2024.1
$ZV: IRIS for UNIX (Ubuntu Server LTS for ARM64 Containers) 2024.1.2 (Build 398U) Thu Oct 3 2024 14:29:04 EDT
Discussion (5)1
Log in or sign up to continue

It appears once a request is dispatched, that's really it.  The rest of the URL seems to parsed out with the arguments and the correct endpoint with args is called.

It seems the convention is that AccessCheck is only called ONCE from the dispatch class.

I can't seem to find clever way to get around this. It would be more helpful to override AccessCheck at the forwarded routes because then there's less parsing of URL to determine where I'm at and what's going on and what permissions I need to check and so on an so forth.

@Eduard Lebedyuk thank you!

I'm not sure that will work: even if I extend my implementation class with my dispatch class, the implementation class doesn't seem to fire off the Page() method which would call AccessCheck().  So Adding the ##SUPER() call into my subclassed AccessCheck() won't work because the subclass Page() and thus AccessCheck() methods are never called.

I think I'm trying to do something this wasn't designed to do? My dispatch class extends %CSP.REST which implements AccessCheck() and then forwards to an implementation class which also extends %CSP.REST. I don't see why the forward wouldn't call the %CSP.REST.Page() method but it's hard to track in the %CSP package.  

Am I understanding your suggestion on how to user ##SUPER() here?

Is there a way to call common code in a %RegisteredObject?  Like an OnBefore method (I don't see anything like that but maybe it happens a different way).  I think my issue is that Page() dispatches the request by calling the implementation method directly so any %CSP.REST or %CSP.Page functions aren't triggered.  I could add a middle layer to the dispatch and the implementation but then I still have the same problem: I want lower level access checks in the implementation method to trigger every time without having to call the access check in each method.  

I think the answer is you don't implement AccessCheck() in an implementation class.  Looking at some documentation examples, classes that extend %REST.impl don't extend %CSP.REST and the latter is where AccessCheck() exists and is called.

I think to get closer to what I want you'd forward your calls to the dispatch class to a lower level dispatch class which may call %CSP.REST.Page() (I haven't tested this yet).  But my goal was implementation level access checks because we have row level security in a multi tenant database. Since often you'd find a object ID in a URL (/csp/testApp/coffeePots/:coffeePotId) and, in my little example here, coffeePotId is most easily accessible in the method implementation itself, the only way to check the access to that ID at a higher level would be to do some tricky URL parsing that the %CSP.REST methods already do so well behind the scenes.

My solution was to create a XData block of all the mappings from objectClass -> HTTP Method -> associatedPermission (e.g. CoffeePots -> POST -> Can create coffee pot) and then use that in a generic method that can be called in each implementation to check permissions.

On another note, you don't want me writing a best REST API practices article for the next ISC article contest, lol.