IAM 2.3.3 added
This repository contains the materials and some examples you can use to learn the basic concepts of REST and IAM.
You can find more in-depth information in https://learning.intersystems.com.
You need to setup your access to InterSystems Container Registry to download IRIS limited access images.
Have a look at this Introducing InterSystems Container Registry on Developer Community.
docker login -u="user" -p="token" containers.intersystems.com
docker pull containers.intersystems.com/intersystems/iris:2023.1.1.380.0
docker pull containers.intersystems.com/intersystems/iam:3.0.2.0-4
IMPORTANT! Copy your InterSystems IRIS IAM enabled license file into the workshop root and rename it to iris.key
.
Build the image we will use during the workshop:
$ git clone https://github.com/intersystems-ib/workshop-rest-iam
$ cd workshop-rest-iam
$ docker-compose build
docker-compose up -d irisA tools
superuser
/SYS
.%Persistent
and %JSON.Adaptor
.%JSON.Adaptor
and transforming objects to and from JSON, check this great article JSON Enhancements on Developer Community.^%REST
wizard.WEBINAR > do ^%REST REST Command Line Interface (CLI) helps you CREATE or DELETE a REST application.Enter an application name or (L)ist all REST applications (L): L Applications Web Applications ------------ ---------------- Enter an application name or (L)ist all REST applications (L): Webinar.API.Leaderboard.v1 REST application not found: Webinar.API.Leaderboard.v1 Do you want to create a new REST application? Y or N (Y): Y File path or absolute URL of a swagger document. If no document specified, then create an empty application. OpenAPI 2.0 swagger: /https://github.com/intersystems-ib/workshop-rest-iam/blob/master/shared/leaderboard-api-v1.json
OpenAPI 2.0 swagger document: /https://github.com/intersystems-ib/workshop-rest-iam/blob/master/shared/leaderboard-api-v1.json
Confirm operation, Y or N (Y): Y
-----Creating REST application: Webinar.API.Leaderboard.v1-----
CREATE Webinar.API.Leaderboard.v1.spec
GENERATE Webinar.API.Leaderboard.v1.disp
CREATE Webinar.API.Leaderboard.v1.impl
REST application successfully created.Create a web application for the REST application? Y or N (Y): Y
Specify web application name. Default is /csp/Webinar/API/Leaderboard/v1
Web application name: /leaderboard/api/v1
-----Deploying REST application: Webinar.API.Leaderboard.v1-----
Application Webinar.API.Leaderboard.v1 deployed to /leaderboard/api/v1
Webinar.API.Leaderboard.v1.impl
.ClassMethod addPlayer(body As %DynamicObject) As %DynamicObject
{
set player = ##class(Webinar.Data.Player).%New()
do player.%JSONImport(body)
set sc = player.%Save()
if $$$ISERR(sc) {
do ..%SetStatusCode(405)
quit ""
}
do player.%JSONExportToStream(.stream)
quit stream
}
ClassMethod getPlayers() As %DynamicObject { set sql = "SELECT Id, Name, Alias FROM Webinar_Data.Player order by Score" set statement = ##class(%SQL.Statement).%New() set sc = statement.%Prepare(sql) set rs = statement.%Execute()
set array = [] while rs.%Next() { do array.%Push( { "Id": (rs.%Get("Id")), "Name": (rs.%Get("Name")), "Alias": (rs.%Get("Alias")), "Node": ($system.INetInfo.LocalHostName()) }) } quit array
}
ClassMethod getPlayerById(playerId As %Integer) As %DynamicObject
{
set player = ##class(Webinar.Data.Player).%OpenId(playerId)
if '$isobject(player) {
do ..%SetStatusCode(404)
quit ""
}
do player.%JSONExportToStream(.stream)
quit stream
}
ClassMethod updatePlayer(playerId As %Integer, body As %DynamicObject) As %DynamicObject
{
set player = ##class(Webinar.Data.Player).%OpenId(playerId)
if '$isobject(player) {
do ..%SetStatusCode(404)
quit ""
}
do player.%JSONImport(body)
do player.%Save()
do player.%JSONExportToStream(.stream)
quit stream
}
ClassMethod deletePlayer(playerId As %Integer) As %DynamicObject
{
set sc = ##class(Webinar.Data.Player).%DeleteId(playerId)
if $$$ISERR(sc) {
do ..%SetStatusCode(404)
}
quit ""
}
/leaderboard/api/v1
in Web Applications. Set unauthenticated access and set Webinar
temporal role.GET Player
, GET Players
, POST Player
y PUT Player
.You’ll plug the API implementation into a production, so you could use any feature of interoperability productions withing a REST API implementation.
Webinar.Production
.Production Settings
and in Settings tab make sure Testing enabled
is checked.Webinar.BO.DummyREST
. Then, in the Settings tab configure:
mockbin.com
/request
(%REST.Impl, Ens.BusinessService)
. Now, you have turned your REST API into a Business Service.Webinar.API.Leaderboard.v1.impl
. Keep it disabled.getPlayerById
implementation:ClassMethod getPlayerById(playerId As %Integer) As %DynamicObject { set player = ##class(Webinar.Data.Player).%OpenId(playerId) if '$isobject(player) { do ..%SetStatusCode(404) quit "" }
// instantiate Business Service (interoperability framework) set sc = ##class(Ens.Director).CreateBusinessService("Webinar.API.Leaderboard.v1.impl",.service) $$$ThrowOnError(sc) // build request message set req = ##class(Ens.StringContainer).%New() set req.StringValue = playerId // send message to Business Operation set sc = service.SendRequestSync("Webinar.BO.DummyREST", req, .rsp) $$$ThrowOnError(sc) // concatenate Business Operation response to REST API response set player.Name = player.Name_"("_rsp.StringValue_")" do player.%JSONExportToStream(.stream) quit stream
}
Webinar.API.Leaderboard.v1.impl
Business Service in production configuration page.GET Player
request and check Message Viewer messages and visual trace.getPlayerById
to continue. Test again the GET Player
request in Postman and check it’s OK.Run API manager container and access IAM Management Portal:
cd iam
docker-compose up -d
Now, you will build a basic scenario to manage the REST API in InterSystems API Manager (IAM).
Remember IAM can be managed using the UI or using the REST interface.
Tip: open a VS Code Terminal session and type the following so you can send curl
commands to IAM.
docker exec -it tools sh
curl -X POST --url http://iam:8001/services/ \
--data 'name=iris-leaderboard-service' \
--data 'url=http://irisA:52773/leaderboard/api/v1' | jq
curl -X POST --url http://iam:8001/services/iris-leaderboard-service/routes \
--data 'paths[]=/leaderboard' \
--data 'name=leaderboard-GET' \
--data 'methods[]=GET'| jq
curl -X POST --url http://iam:8001/services/iris-leaderboard-service/routes \
--data 'paths[]=/leaderboard' \
--data 'name=leaderboard-POST' \
--data 'methods[]=POST'| jq
curl -X POST --url http://iam:8001/services/iris-leaderboard-service/routes \
--data 'paths[]=/leaderboard' \
--data 'name=leaderboard-PUT' \
--data 'methods[]=PUT'| jq
curl -i -X POST \
--url http://iam:8001/services/iris-leaderboard-service/plugins \
--data 'name=request-transformer' \
--data 'config.add.headers=Authorization:Basic c3VwZXJ1c2VyOlNZUw==' \
--data 'config.replace.headers=Authorization:Basic c3VwZXJ1c2VyOlNZUw=='
IAM - Get Player - No auth
request.key-auth
plugin in the service.curl -X POST http://iam:8001/services/iris-leaderboard-service/plugins \
--data "name=key-auth" | jq
IAM - Get Player - No auth
request.systemA
curl -d "username=systemA&custom_id=SYSTEM_A" http://iam:8001/consumers/ | jq
curl -X POST http://iam:8001/consumers/systemA/key-auth -d 'key=systemAsecret' | jq
IAM - GET Player. Consumer SystemA
request.webapp
curl -d "username=webapp&custom_id=WEB_APP" http://iam:8001/consumers/ | jq
webapp
curl -X POST http://iam:8001/consumers/webapp/key-auth -d 'key=webappsecret' | jq
IAM - GET Players - Consumer WebApp
request.docker exec -it tools sh
/https://github.com/intersystems-ib/workshop-rest-iam/blob/master/shared/simulate.sh
webapp
consumer. Limit it to 100 requests in a minute.curl -X POST http://iam:8001/consumers/webapp/plugins \
--data "name=rate-limiting" \
--data "config.minute=100" | jq
Dev Portal > Settings
:Authentication Plugin=Basic
Auto Approve Access=Enable
Session Config (JSON)=Custom
and enter:{
"cookie_name": "portal_session",
"secret": "CHANGE_THIS",
"storage": "kong",
"cookie_secure": false
}
Dev Portal > Editor
New File +
and set File Type=spec
and File Path=leaderboard.yaml
.Sign Up
.Create API Credential
.IAM - Get Players - Developer
replacing the api-key
header by the actual credential you have just created.Documentation
.shared/audit.json
file.curl -X POST http://iam:8001/plugins/ \
--data "name=http-log" \
--data "config.http_endpoint=http://irisA:52773/audit/log" \
| jq
In this scenario, you will need a second IRIS instance:
docker-compose up -d irisB
You will build a load balancing scenario between two IRIS instances with the leaderboard REST API.
This can be useful in case you want to spread the workload, blue-green deployment, etc.
Tip: open a VS Code Terminal session and type the following so you can send curl
commands to IAM.
docker exec -it tools sh
curl -s -X POST http://iam:8001/upstreams \
-d name=leaderboard-lb-stream \
| jq
curl -s -X POST http://iam:8001/upstreams/leaderboard-lb-stream/targets \
-d target=irisA:52773 \
-d weight=500 \
| jq
curl -s -X POST http://iam:8001/upstreams/leaderboard-lb-stream/targets \
-d target=irisB:52773 \
-d weight=500 \
| jq
curl -X PATCH http://iam:8001/services/iris-leaderboard-service \
--data host='leaderboard-lb-stream' | jq
IAM - GET Players (LB)
request. Pay attention to the Node
property in the response body.In this scenario, you will need a third iris instance:
docker-compose up -d irisC
You will now build a route by header scenario using three IRIS instances with the leaderboard REST API.
This could be useful in case you want use different servers depending on request headers (e.g. different versions).
Tip: open a VS Code Terminal session and type the following so you can send curl
commands to IAM.
docker exec -it tools sh
curl -s -X POST http://iam:8001/upstreams \
-d name=leaderboard-header-stream \
| jq
curl -s -X POST http://iam:8001/upstreams \
-d name=leaderboard-header-v1-stream \
| jq
curl -s -X POST http://iam:8001/upstreams \
-d name=leaderboard-header-v2-stream \
| jq
curl -s -X POST http://iam:8001/upstreams/leaderboard-header-stream/targets \
-d target=irisA:52773 \
| jq
curl -s -X POST http://iam:8001/upstreams/leaderboard-header-v1-stream/targets \
-d target=irisB:52773 \
| jq
curl -s -X POST http://iam:8001/upstreams/leaderboard-header-v2-stream/targets \
-d target=irisC:52773 \
| jq
curl -X PATCH http://iam:8001/services/iris-leaderboard-service \
--data host='leaderboard-header-stream' | jq
route-by-header
plugin with some conditions on request header version
:curl -s -X POST http://iam:8001/services/iris-leaderboard-service/plugins \
-H 'Content-Type: application/json' \
-d '{"name": "route-by-header", "config": {"rules":[{"condition": {"version":"v1"}, "upstream_name": "leaderboard-header-v1-stream"}, {"condition": {"version":"v2"}, "upstream_name": "leaderboard-header-v2-stream"}]}}' \
| jq
IAM - GET Players (Route By Header)
using different version
header request values.decK helps manage Kong’s configuration in a declarative fashion. This means that a developer can define the desired state of Kong Gateway – services, routes, plugins, and more – and let decK handle implementation without needing to execute each step manually, as you would with the Kong Admin API.
docker exec -it tools sh
cd /tmp
curl -sL https://github.com/Kong/deck/releases/download/v1.17.3/deck_1.17.3_linux_arm64.tar.gz -o deck.tar.gz
tar -xf deck.tar.gz -C /tmp
cp /tmp/deck /usr/local/bin/
deck dump --kong-addr http://iam:8001
Now have a look at the file kong.yaml
.
deck diff --kong-addr http://iam:8001
kong.yaml
run the following:deck sync --kong-addr http://iam:8001
keycloak
to 127.0.0.1127.0.0.1 keycloak
You can find your hosts file in:
O.S. | File |
---|---|
MacOS | /private/etc/hosts |
Windows | c:\Windows\System32\Drivers\etc\hosts |
Run Keycloak as your Identity Provider:
cd keycloak
docker-compose up -d
admin
/ test
We are going to use the default “master” realm.
Create a client that will represent IAM (API Manager).
Click on Clients
Click on Create
Create a new client:
iam
Edit iam
client:
confidential
On
https://iam:8443
/oidc-route/*
Click on Credentials tab and copy Secret:
Create a user that you will use to login when using your API:
Click on Users and Add User:
Enter the following:
test
Click on Credentials tab and set a new password:
Off
curl -X POST --url http://iam:8001/services/ \
--data 'name=oidc-service' \
--data 'url=http://irisA:52773/rest/GenericService/anything' | jq
curl -X POST --url http://iam:8001/services/oidc-service/routes \
--data 'paths[]=/oidc-route' \
--data 'name=oidc-route' \
--data 'methods[]=GET'| jq
IAM > Routes > oidc-route > Add Plugin > OpenID Connect
Client ID: iam
Client Secret:
Issuer (Discovery Document URI): https://keycloak:7443/auth/realms/master/.well-known/openid-configuration
Test the route again, now it should prompt a Keycloack Login Page where you can login with the user you created and finally access the API: https://iam:8443/oidc-route/somedata