検索

Article
· 6 hr ago 1m read

Using IRIS as a vector database

InterSystems IRIS embedded vector search capabilities lets us search unstructured and semi-structured data. Data is converted to vectors (also called ‘embeddings’) and then stored and indexed in InterSystems IRIS for semantic search, retrieval-augmented generation (RAG), text analysis, recommendation engines, and other use cases.

This is a simple demo of IRIS being used as a vector database and similarity search on IRIS.

Prerequisites:

  1. Python
  2. InterSystems IRIS for Health - as it will be used as the vector database

Repository: https://github.com/piyushisc/vectorsearchusingiris

Steps to follow:

  1. Clone the repo.
  2. Open VS Code, connect to desired instance and namespace of IRIS and compile the classes.
  3. Open IRIS Terminal and invoke the command do ##class(vectors.vectorstore).InsertEmbeddings(), which reads the text from the file text.txt and generate embeddings and store them in IRIS.
  4. Invoke the command do ##class(vectors.vectorstore).VectorSearch("search_terms") with desired words to perform similarity search. IRIS will return top three closest match: alt text
Discussion (0)1
Log in or sign up to continue
Article
· 7 hr ago 6m read

Securing FHIR Server with OAuth 2.0 using IAM

InterSystems API Manager (IAM) is a core component of the InterSystems IRIS Data Platform, offering centralized API management with a strong emphasis on security. IAM simplifies the entire API lifecycle, from creation to retirement, and provides a developer portal for easy API discovery and integration. Access control features allow administrators to define precise permissions, and IAM seamlessly integrates with the IRIS Data Platform, enhancing data management and integration capabilities.

Features of IAM include:

  • API Gateway: Centralized API management and security hub.
  • API Lifecycle Management: Complete lifecycle control from creation to retirement.
  • Security: Authentication, authorization, and data encryption.
  • Monitoring and Analytics: Tools for usage monitoring and pattern analytics.
  • Developer Portal: API discovery portal with documentation and testing.
  • Access Control: Granular control over API access and actions.
  • Integration with InterSystems IRIS: Seamless integration with IRIS Data Platform.

Use case: The use case in this report is Identity and Access Management.

Authentication and authorization adhering to OAuth 2.0 standard, securing a FHIR server using IAM.

In this document, you will learn how to secure a FHIR Server with OAuth 2.0 using InterSystems API Manager. OAuth 2.0 is a widely used standard for authorization that enables applications to access protected resources on a FHIR server. InterSystems API Manager is a tool that simplifies the creation, management, and monitoring of FHIR APIs. By following the steps in this document, you will be able to configure InterSystems API Manager to act as an OAuth 2.0 authorization server and grant access tokens to authorized clients. You will also learn how to use client libraries to connect your application to the FHIR server using OAuth 2.0.

Note: FHIR server only supports JWT tokens for OAuth 2.0 authentication, does not support opaque tokens.

Instructions to run the demo locally:

  1. Run the following command in Command Prompt to clone the relevant repository:
    git clone https://github.com/isc-padhikar/IAM_FHIRServer
  2. Go into the directory of newly clone repository and create a new directory and name it 'key'. And copy a iris.key file, which is the license for InterSystems IRIS for Health which supports API Management.
  3. Then go back to Command Prompt and run the following commands one by one:
    docker-compose build
    docker-compose up
  4. Go to localhost:8002 which has IAM running.
  5. Using IAM, I can make a FHIR server available as a service like seen in the picture below:
  6. Define a route that will be the proxy for the FHIR server (I have defined /fhir as the proxy) like in the picture below:
  7. And, define plugins that will handle the incoming requests to the FHIR server, authenticate and authorize access to the FHIR server. We should define the issuer of JWT token (the authorization server) and the public key that we obtain by decoding private key (please refer to the upcoming 'Authorization server' section for this part), in the JWT plugin under 'Credentials' section like in the following images: Following images show authentication using Auth0 server and authorization based JWT tokens via IAM. Getting a JWT token from authorization server: Using the JWT token to access FHIR server via proxy route defined in IAM:

Authorization server:

An external authorization server is used and its Auth0. The instructions to set up an authorization server is given in the README of demo #1 (FHIROktaIntegration) mentioned in upcoming 'Demos used as reference' section.

Endpoint to get JSON Web Key Set (JWKS): https://dev-bi2i05hvuzmk52dm.au.auth0.com/.well-known/jwks.json

It provides us with a pair of keys for the authorization server that we've set up and that can be used to retrieve the private key using a decoding algorithm.

We will use the private key in IAM to verify JWT token signatures.

Best practice to retrieve public key from a JWKS is using a programming language. I used following code in Python:

import base64
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
import requests
# Replace 'YOUR_DOMAIN' with your actual Auth0 domain
jwks_url = 'https://dev-bi2i05hvuzmk52dm.au.auth0.com/.well-known/jwks.json'
response = requests.get(jwks_url)
jwks = response.json()
# Choose a specific key from the JWKS (e.g., the first key)
selected_key = jwks['keys'][0]
# Decode 'AQAB' (exponent 'e') from Base64 URL-safe to integer
decoded_exponent = int.from_bytes(base64.urlsafe_b64decode(selected_key['e'] + '==='), byteorder='big')
decoded_modulus = int.from_bytes(base64.urlsafe_b64decode(selected_key['n'] + '==='), byteorder='big')
# Construct the RSA public key
public_key = rsa.RSAPublicNumbers(
    decoded_exponent,
    decoded_modulus
).public_key(default_backend())
# Convert the public key to PEM format
public_key_pem = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)
print(public_key_pem.decode('utf-8'))

 

Demos used as reference:

  1. FHIROktaIntegration: https://openexchange.intersystems.com/package/FHIROktaIntegration This demo shows how to configure OAuth 2.0 directly on InterSystems IRIS for Health and use that configuration for a FHIR server. Please follow the instructions it has to configure authorization server's details. However, the configurations looks like this in the management portal after done: It has a Angular app that authenticates with the authorization server, with a UI that displays FHIR resources after authorization.   This demonstrate how OAuth2.0 can be configured within InterSystems IRIS for Health to secure a FHIR server.  
  2. IAM Zero-to-Hero: https://openexchange.intersystems.com/package/iam-zero-to-hero

The demo constitutes of IAM, and IAM related training. I will be modifying this to have a FHIR server and using the instance of IAM in this demo to authentication with Auth0 authorization server and authorize access using JWT plugin.
Unlike the previous demo, this demonstrated the use of IAM to expose a FHIR server endpoint and secure it by OAuth 2.0 standard using the plugins library that IAM offers.

Changes made in this demo:

1. I added a FHIR server in the instance of IRIS for Health in this demo. Please replace the code in iris.script file with this following code:

;do $System.OBJ.LoadDir("/opt/irisapp/src","ck",,1)
zn "%SYS"
Do ##class(Security.Users).UnExpireUserPasswords("*")
set $namespace="%SYS", name="DefaultSSL" do:'##class(Security.SSLConfigs).Exists(name) ##class(Security.SSLConfigs).Create(name) set url="https://pm.community.intersystems.com/packages/zpm/latest/installer" Do ##class(%Net.URLParser).Parse(url,.comp) set ht = ##class(%Net.HttpRequest).%New(), ht.Server = comp("host"), ht.Port = 443, ht.Https=1, ht.SSLConfiguration=name, st=ht.Get(comp("path")) quit:'st $System.Status.GetErrorText(st) set xml=##class(%File).TempFilename("xml"), tFile = ##class(%Stream.FileBinary).%New(), tFile.Filename = xml do tFile.CopyFromAndSave(ht.HttpResponse.Data) do ht.%Close(), $system.OBJ.Load(xml,"ck") do ##class(%File).Delete(xml)

//init FHIR Server
zn "HSLIB"
set namespace="FHIRSERVER"
Set appKey = "/csp/healthshare/fhirserver/fhir/r4"
Set strategyClass = "HS.FHIRServer.Storage.Json.InteractionsStrategy"
set metadataPackages = $lb("hl7.fhir.r4.core@4.0.1")
set importdir="/opt/irisapp/src"
//Install a Foundation namespace and change to it
Do ##class(HS.Util.Installer.Foundation).Install(namespace)
zn namespace

// Install elements that are required for a FHIR-enabled namespace
Do ##class(HS.FHIRServer.Installer).InstallNamespace()

// Install an instance of a FHIR Service into the current namespace
Do ##class(HS.FHIRServer.Installer).InstallInstance(appKey, strategyClass, metadataPackages)

// Configure FHIR Service instance to accept unauthenticated requests
set strategy = ##class(HS.FHIRServer.API.InteractionsStrategy).GetStrategyForEndpoint(appKey)
set config = strategy.GetServiceConfigData()
set config.DebugMode = 4
do strategy.SaveServiceConfigData(config)

zw ##class(HS.FHIRServer.Tools.DataLoader).SubmitResourceFiles("/opt/irisapp/fhirdata/", "FHIRSERVER", appKey)

zn "USER"
zpm "load /opt/irisbuild/ -v":1:1
zpm 
load /opt/irisapp/ -v
q
do ##class(Sample.Person).AddTestData()
halt

2.  In docker-compose.yml file, update IAM's image to latest (containers.intersystems.com/intersystems/iam:3.2.1.0-4), because only IAM (Kong) versions form 3.1 support JSON draft-6, which is what FHIR specification provides.

Discussion (0)1
Log in or sign up to continue
Article
· 8 hr ago 2m read

"The Hidden HTTP Errors" (Behind IIS)

You send an HTTP request and get back an HTTP error but with an HTML error page which you didn't expect... what's happening?... 🤔

Specifically for example, perhaps you tried to READ a FHIR Resource (e.g. /Patient/123) and you get back a 404 error page, even though with other Patient IDs, you get back the Resource payload, so "the page" definitely does exist... why should you be getting a 404 error page? 🙄

The answer to these questions is related to the IIS Web Server behavior with regard to handling errors.

IIS has 3 options for displaying errors:

  • Always only show custom error pages
  • Always show detailed server errors
  • For local requests show detailed errors, but for remote requests show the custom error pages.

This last option is more secure (than always showing detailed errors) because sometimes the error details might expose internal info you don't want external users to have. Hence this is the default for IIS.

But that means that if you are testing against a remote server the actual errors will be hidden from you. So you would want to change this to be 'Detailed errors' (at least for debugging stages, and assuming you are taking into account external access, and perhaps limiting it).

See more details about this setting in a related IIS article (and you can see this related section in our Documentation that discusses this).

The specific FHIR example I mentioned is an interesting case, as a 404 error could simply mean that a specific FHIR Resource was not found (the ID you were trying to read is not in the Repository), not that you have some problem with the server ("page not found").

For example if you can see the detailed error you will see something like this:

But without the detailed error, you would get just a custom error page, like this:

And this might be misleading, so keep this IIS setting in mind.

Discussion (0)1
Log in or sign up to continue
Article
· 15 hr ago 5m read

在 InterSystems IRIS 中创建和集成 JWT

什么是 JWT?

JWT (JSON Web Token,JSON 网络令牌)是一种开放标准(RFC 7519),它提供了一种轻量级、紧凑、自足的方法,用于在双方之间安全地传输信息。它常用于网络应用程序中的身份验证、授权和信息交换。

JWT 通常由三部分组成:

1.JOSE(JSON Object Signing and Encryption,JSON 对象签名和加密)标头
2.有效载荷
3.签名

这些部分以 Base64Url 格式编码,并用点(.)分隔。

JWT 的结构

标题

{ "alg": "HS256", "typ": "JWT"}

有效载荷

{"sub": "1234567890", "name": "John Doe", "iat": 1516239022}

签名
签名用于验证 JWT 的发件人是否为其本人,并确保信息未被篡改。

创建签名

1. base64 编码报头和有效载荷。
2.使用秘钥(对于对称算法,如 HMAC)或私钥(对于非对称算法,如 RSA)应用签名算法(如 HMAC SHA256 或 RSA)。
3.对结果进行 Base64Url 编码,以获得签名。

JWT 样本。查看JWT 内容

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

在 IRIS 中创建 JWT

注:2024 年以前,%OAuth2.JWT类用于在 IRIS 中生成 JWT。现在,%Net.JSON.JWT类是创建 JWT 的主要类,我将在示例代码中使用该类。

JWK 概述

JWK 表示加密密钥,特别用于签署和验证 JWT。通过 JWK,您可以用一种标准化格式来表示公钥(用于验证)和私钥(用于签名),以便在系统间轻松交换。JWKS 拥有多个 JWK

JWT 工作流程

1.以%DynamicObject 的形式构建头信息,并根据需要添加自定义头信息

2.直接以 %DynamicObject 构建正文/注释

3.调用 %Net.JSON.JWT 类的Create 方法

Set sc = ##Class(%Net.JSON.JWT).Create(header, , claims, jwks, , .JWT)

创建 JWK

Set sc = ##Class(%Net.JSON.JWK).Create("HS256","1212ASD!@#!#@$@#@$$#SDFDGD#%+_)(*@$SFFS",.privateJWK,.publicJWK)

这将返回私钥

{"kty":"oct","k":"MTIxMkFTRCFAIyEjQCRAI0AkJCNTREZER0QjJStfKSgqQCRTRkZT","alg":"HS256"

一些重要的 JWK 属性

"kty":"oct"- 表示 对称 算法
"kty":"RSA" / "kty":"EC"- 表示 非对称 算法

创建 JWK 后,就可以将其添加到JWKS 中。

让我们在 IRIS 中创建 JWKS

Set sc = ##class(%Net.JSON.JWKS).PutJWK(jwk,.JWKS)

此方法返回 JWKS

在 IRIS 中生成 JWT

您可以在 IRIS 中创建对称或非对称密钥 JWT。%Net.JSON.JWK 类主要用于生成 JWT。 在调用该方法之前,请确保在生成JWT 时为对称加密和非对称加密创建并发送了JWKS

对称加密

对称算法使用共享密钥,发送方和接收方都使用相同的密钥来签署和验证 JWT。这些算法,如 HMAC(HS256、HS512、HS384),会为 JWT 有效负载生成散列(签名)。不建议高安全性系统采用这种方法,因为签名和验证都暴露在外,会带来潜在的安全风险。

%Net.JSON.JWK 类中的 Create 方法用于生成 JWK。该方法接收两个输入参数,并返回两个输出参数:

1.算法 - 用于创建 JWK 的算法。
secert - 用于签署和验证 JWT 的密钥。
privateJWK - 创建的私人 JSON 网络密钥。
publicJWK - 创建的公共 JSON Web 密钥。

For symmetric key algorithms - you'll get privateJWK

for Asymmetric key algorithms- You'll get privateJWK and publicJWK
 
对称密钥 JWT

输出

LEARNING>d ##class(Learning.JWT.NetJWT).SymmetricKeyJWT()
privateJWK={"kty":"oct","k":"MTIxMkFTRCFAIyEjQCRAI0AkJCNTREZER0QjJStfKSgqQCRTRkZT","alg":"HS256"}  ; <DYNAMIC OBJECT>
privateJWKS="{""keys"":[{""kty"":""oct"",""k"":""MTIxMkFTRCFAIyEjQCRAI0AkJCNTREZER0QjJStfKSgqQCRTRkZT"",""alg"":""HS256""}]}"
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsIngtYyI6InRlIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.PcCs_I8AVy5HsLu-s6kQYWaGvuwqwPAElIad11NpM_E

非对称加密

非对称加密是指使用一对密钥:一个密钥用于签署令牌(私钥),另一个密钥用于验证令牌(公钥)。这与对称加密不同

私钥:该密钥用于签署 JWT。它是保密的,绝对不能暴露。
公钥:此密钥用于验证 JWT 的真实性。它可以安全地共享和分发,因为它不能用来签署新的标记。

您可以通过%SYS.X509Credentials 生成带有私钥/证书的 JWT 非对称加密。

 
非对称 X509

网络应用中的 JWT

2023 版开始,IRIS 默认为网络应用程序内置 JWT 创建功能。在设置网络应用程序时,确保启用 JWT 身份验证

我已添加了有关配置的简要说明

1.在网络应用程序中启用JWT 身份验证
2.如果还没有,创建一个 REST 类
3.包含默认端点资源"/login"。使用基本身份验证调用 REST API,有效载荷为 {"user":"_SYSTEM","password":"SYS"}。
4.4. 响应将是一个 JSON 文件,其中包含 "access_token"、"refresh_token "和其他相关详细信息。
5.5. 使用 "access_token "进行授权。

Discussion (0)1
Log in or sign up to continue
Article
· Dec 23 3m read

Minha experiência com APIs e integração de POS

Olá amigo! 😊 Como você está hoje?

Gostaria de compartilhar uma pequena parte dos meus aprendizados no meu primeiro projeto oficial: a integração de máquinas de POS/EDC com nosso sistema de faturamento. Foi um desafio empolgante onde tive experiência prática trabalhando com APIs e fornecedores.

Como uma máquina de pagamento realmente funciona?

É simples: comece iniciando/criando uma transação e, depois, recupere o status do pagamento dela.

Aqui, iniciar/criar refere-se ao método POST, e recuperar refere-se ao GET.

Fluxo de trabalho...

Vamos assumir que o fornecedor nos forneceu um documento com ambas as APIs (Criar e Buscar Status do Pagamento). Exemplos listados abaixo:

 

CRIAR TRANSAÇÃO:

url/endpoint: https://payvendor.com/create-transaction
method: POST
payload: 
{
    "reference_id": "2345678",
    "pos_id": "PISC98765",
    "date_time": "MMDDYYYYHHMMSS"
    "amount": 100
}
response: [200]
{
    "reference_id": "2345678",
    "pos_id": "PISC98765",
    "date_time": "MMDDYYYYHHMMSS"
    "unn": "456789876546787656"
}

BUSCAR STATUS DO PAGAMENTO:

url/endpoint: https://payvendor.com/get-status
method: GET
payload: ?reference_id="2345678"
response: [200]
{
    "reference_id": "2345678",
    "pos_id": "PISC98765",
    "date_time": "MMDDYYYYHHMMSS"
    "unn": "456789876546787656"
    "status": "paid"
    "amount": 100
}

 

Como usamos essas APIs? Vamos descobrir... 🫡

Para consumir essas APIs no ObjectScript, temos um módulo ou classe para realizar requisições HTTP internamente: %Net.HttpRequest.

O básico:

  • Criar uma instância de %Net.HttpRequest.
  • Definir a URL e o método HTTP.
  • Adicionar o cabeçalho (header) e o corpo (body). [se necessário]
  • Enviar a requisição para o servidor.
  • Tratar a resposta.
; --------- EXEMPLO DE REQUISIÇÃO POST ---------
Set req = ##class(%Net.HttpRequest).%New()  ; cria uma instância desta classe
Set req.Server = "https://payvendor.com"    ; o servidor
Set req.Location = "/create-transaction"    ; o endpoint
Set req.Https = 1                           ; 0 para http / 1 para https
Set req.ContentType = "application/json"    ; Tipo de conteúdo
; ---- create the JSON body ----
Set obj = ##class(%DynamicObject).%New()
Set obj."reference_id" = "2345678"      ; ID único
Set obj."pos_id" = "PISC98765"          ; número do dispositivo
Set obj."date_time" = $ZSTRIP($ZDATETIME($HOROLOG,8), "*P") 
Set obj."amount" = 100
; -------------------------------
; ---- enviar requisição ----
Do req.EntityBody.Write(obj.%ToJSON())
Do req.Post()           ; .Post() irá disparar a chamada
; ----------------------
; ---- Resposta ----
Write req.HttpResponse.StatusCode,!     ; CÓDIGO DE STATUS HTTP
Write req.HttpResponse.Data.Read(),!    ; MENSAGEM DE STATUS HTTP
; ------------------

Após criar a transação, podemos manter uma tabela (preferível) ou um global para manter os logs de cada transação.

; --------- EXEMPLO DE REQUISIÇÃO GET ---------
Set req = ##class(%Net.HttpRequest).%New()  ; cria uma instância desta classe
Set req.Server = "https://payvendor.com"    ; o servidor
Set req.Location = "/get-status"            ; o endpoint
Set req.Https = 1                           ; 0 para http / 1 para https
; ---- Parâmetros de Consulta (Query) ----
Do req.SetParam("reference_id", "2345678")

; ---- enviar requisição ----
Do req.Get()           ; .Get() irá disparar a chamada
; ---- Resposta ----
Set stsCode = req.HttpResponse.StatusCode,!     ; CÓDIGO DE STATUS HTTP
If stsCode=200 {
    Set objResponse = req.HttpResponse.Data.Read()
    Set objData = ##class(%DynamicObject).%FromJSON(objResponse)
    Set payStatus = objData.status              ; status do pagamento
}
; ------------------

É assim que buscamos o status do pagamento. Depois de obter o status, podemos atualizá-lo no sistema de faturamento e também em nossos logs.

 

Este fluxo de trabalho é simples, mas à medida que programamos mais, podemos desenvolver frameworks e abordagens melhores. Ao longo da minha experiência, integrei com sucesso 5 fornecedores de POS e 3 gateways de pagamento com nosso sistema de faturamento. Se você tiver alguma dúvida ou precisar de orientação, sinta-se à vontade para entrar em contato!

Também estou aberto a feedbacks. :)

Obrigado...

Discussion (0)1
Log in or sign up to continue