Written by

Software Architect at Visum
Article Yuri Marx · Nov 25, 2021 3m read

The power of XDATA applied to the API Security

The XData (https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GOBJ_XDATA) is a powerful feature to set documentation and metadata information for classes and methods. The %CSP.REST class uses XDATA to mapping REST calls (https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GREST_csprest), so in this article you will see how to use XData into your apps as code, not only as documentation.

When you write XData comments/definitions, the IRIS store it into %Dictionary.ClassDefinition (for classes) %Dictionary.MethodDefinition (for methods). If you query these tables, you will be able get metadata information and write code to this metadata configuration. %CSP.REST do this when you write your REST mappings for your REST Services using ObjectScript.

I wrote an application that is using XDATA to enforce authorization rules to the class method endpoints, see:

///Retreive all the records of dc.Sample.Person
///@security.and: roles: { PersonAdmin }  
ClassMethodGetAllPersons()As%Status
{
 
    #dimtSCAs%Status=$$$OK
....
}

The @security.and does not exists into IRIS. So I need to read this configuration and write code to enforce access to the users with PersonAdmin role only.

To get this @security.and, you need to read this XData. See:

ClassMethodGetXDataContent(className,methodName)As%String
{
     
    SetqryXdata="SELECT parent, Name, Description FROM %Dictionary.MethodDefinition WHERE parent = ? and Name = ?"
    SetstmXdata=##class(%SQL.Statement).%New()
    SetqStatus=stmXdata.%Prepare(qryXdata)
    IfqStatus'=1{Write"%Prepare  failed:"Do$System.Status.DisplayError(qStatus)Quit}
    SetrsetXdata=stmXdata.%Execute(className,methodName)

 

    WhilersetXdata.%Next(){
        // Return rsetXdata.Name
        ReturnrsetXdata.Description
    }
}

With this method you be able to get any xdata content for methods.

Now, to restrict access only to the users with the PersonAdmin role is simple. You need to override AccessCheck ClassMethod from %CSP.REST class. See:

ClassMethodAccessCheck(OutputpAuthorizedAs%Boolean=0)As%Status
{
 
  Do##super()
 
  Setmessage={}
 
  SettSC=$$$OK
 
  Setmessage.verb=%request.Method

 

  Setmessage.url=%request.URL

 

  Setmessage.url="/"_$REPLACE(message.url,%request.Application,"")

 

  Setmessage.application=%request.Application
 
  SetmethodName=""
  Do..GetClassMethodName(message.url,%request.Method,.methodName)
 
  Setmessage.method=methodName

 

  Setxdata=##class(dc.SecurityMediator.XDataUtil).GetXDataContent($CLASSNAME(),methodName)

 

  Do..GetSecurityRules(xdata,.rules,.roles,.header,.operator)

 

  SetUserRoles=$LISTFROMSTRING($ROLES,",")
  SetRolesAllowed=UserRoles
 
  If$FIND(xdata,"@security")>0{
    SetRolesAllowed=$LISTFROMSTRING(roles,",")
  }

 

  SetHasRole=0
 
  ForRoleIdx=1:1:$LISTLENGTH(UserRoles){
    If$LISTFIND(RolesAllowed,$LIST(UserRoles,RoleIdx)){
      SetHasRole=1
      Quit
    }
  }

 

  IfHasRole{
    SetpAuthorized=1
  }Else{
    SetpAuthorized=0
    Setmessage.error=$USERNAME_" is not authorized for this request. User Roles Allowed is not in User Roles"
    Writemessage.%ToJSON()
  }

 

  ReturntSC
}

With the rule match, set pAuthorized = 1, otherwise, set 0.

Now the roles allowed is based into XData configuration to your REST Class. Great!

If you want to see this in action, get my new app: https://openexchange.intersystems.com/package/API-Security-Mediator.

Comments

Yuri Marx  Dec 3, 2021 to Iryna Mologa

Thanks Irina!

0
Nigel Salm · Jan 1, 2022

That's very cool and a very useful way of handling security and access to specific CALLS in the REST dispatch class. Impressed

0
Yuri Marx  Jan 2, 2022 to Nigel Salm

Thanks nigel

0