検索

Article
· 22 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
· Dec 23 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
Article
· Dec 23 3m read

Destaque do OAuth do FHIR (2024.3+) – Guia rápido para novos clientes

Também nas versões anteriores era possível configurar o seu servidor FHIR para aceitar requisições via OAuth 2.0 (por exemplo, para um cliente SMART on FHIR), mas atualmente, com a versãov2024.3, lançada a algum tempo— existe um novo recurso que torna isso mais fácil: o OAuth FHIR Client QuickStart. 

Esse “QuickStart” é um assistente em formato de wizard que permite conectar seu servidor FHIR a um servidor OAuth e habilitar autenticação e autorização OAuth para requisições FHIR em 5 passos simples (na prática, apenas 3…).

  • Passo 1 - Criar ou novo ou escolher o Servidor FHIR

Você pode já ter um servidor FHIR (endpoint) definido, ou ainda não ter definido um e querer configurá-lo agora, como parte deste QuickStart.

  • Passo 2 - Selecione o Servidor FHIR

Se você optar por “Usar um existente”, serão exibidos os endpoints disponíveis, por namespace. Por exemplo:

Se você optar por “Criar novo”, será exibido um pequeno formulário para criar um novo endpoint.

Isso é semelhante ao que você veria caso criasse o endpoint manualmente anteriormente:

  • Passo 3 - Selecione o tipo de servidor OAuth

Você pode optar por usar um servidor OAuth externo (por exemplo, Auth0 da Okta) ou utilizar o servidor OAuth integrado ao InterSystems IRIS.

Se quiser usar o IRIS como seu servidor OAuth, será necessário configurá-lo como um servidor OAuth com suporte a FHIR. Para isso, também existe um “atalho”: um método que você pode chamar e que realiza essa configuração automaticamente.

Observação: Comunicação Segura (Secure Communication) precisa estar configurada para que isso funcione.

 

  • Passo 4 - Configure o servidor OAuth

Se você optar por usar um servidor OAuth externo, será solicitado o endpoint do emissor (Issuer Endpoint):

Se você já tiver definido um, pode escolhê-lo no menu suspenso; caso contrário, pode digitá-lo (ou colá-lo).

De qualquer forma, é possível testar esse endpoint do servidor OAuth, por exemplo:

  • Passo 5 (ou 4 se você escolheu o servidor OAuth interno do IRIS) – Confirmar

Você verá uma pequena confirmação com informações e um botão “Confirmar”.

Por exemplo (ao optar por criar um novo servidor FHIR e usar o servidor OAuth interno do IRIS):

Ou, por exemplo (ao escolher um endpoint FHIR existente e um servidor OAuth externo):

Se tudo correr bem, você verá uma mensagem indicando que a criação foi bem-sucedida.

Caso contrário, receberá uma mensagem apropriada.

Nos bastidores, você poderá observar algumas coisas -

  • Você verá o cliente definido na lista de clientes OAuth (com seus detalhes):

  • Você verá o cliente definido no seu endpoint FHIR:

  • Na seção geral de Segurança do Management Portal, em OAuth 2.0, você também poderá encontrar uma Definição de Cliente com a Configuração do Cliente.

Assumindo que tudo o que foi descrito acima esteja funcionando, você já pode começar a usar OAuth (e, especificamente, SMART on FHIR) para se comunicar com seu servidor FHIR.

Mais detalhes sobre isso... em um artigo futuro...

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

Pergunte às suas classes IRIS usando Ollama, IRIS VectorDB e LangChain.

Se você quiser saber se já existe uma classe sobre um determinado tópico fazendo uma simples pergunta em linguagem natural, isso agora é possível. Baixe e execute a aplicação https://openexchange.intersystems.com/package/langchain-iris-tool para conhecer tudo sobre as classes do seu projeto em um chat.

Instalação:

$ git clone https://github.com/yurimarx/langchain-iris-tool.git
$ docker-compose build
$ docker-compose up -d

Como usar:

1. Abra a URL http://localhost:8501

2. Verifique o botão de Configurações usado pelo agente para se conectar ao InterSystems IRIS.

3. Pergunte sobre as classes que você desenvolveu (por exemplo: Existem classes que herdam de Persistent?)

UI 4

Soluções utilizadas:

  1. Ollama – LLM privado e ferramenta de chat com NLP
  2. LangChain – plataforma para construir agentes de IA
  3. Streamlit – framework de frontend
  4. InterSystems IRIS – servidor para responder às perguntas

Sobre Ollama

É uma solução de LLM gratuita e on-premises que permite executar IA Generativa com privacidade e segurança, pois seus dados são processados apenas localmente. O projeto Ollama oferece suporte a diversos modelos, incluindo Mistral, modelos da OpenAI, modelos DeepSeek, entre outros, todos rodando on-premises. Este pacote utiliza o Ollama via Docker Compose com o modelo Mistral:

ollama:
    image: ollama/ollama:latest
    deploy:
      resources:
        reservations:
          devices:
          - driver: nvidia
            capabilities: ["gpu"]
            count: all  # Adjust count for the number of GPUs you want to use
    ports:
      - 11434:11434
    volumes:
      - ./model_files:/model_files
      - .:/code
      - ./ollama:/root/.ollama
    container_name: ollama_iris
    pull_policy: always
    tty: true
    entrypoint: ["/bin/sh", "/model_files/run_ollama.sh"] # Loading the finetuned Mistral with the GGUF file
    restart: always
    environment:
      - OLLAMA_KEEP_ALIVE=24h
      - OLLAMA_HOST=0.0.0.0

Sobre Langchain:

LangChain é um framework para construir aplicações de IA Generativa de forma simples. O LangChain possui o conceito de tool (ferramenta). As ferramentas são plug-ins (aplicações de RAG) usadas pelo LangChain para complementar o trabalho dos LLMs. Esta aplicação implementa uma ferramenta do LangChain para fazer perguntas de gerenciamento e desenvolvimento para o seu servidor IRIS:

from langchain.llms import Ollama
from langchain.chains import RetrievalQA
from langchain.document_loaders import CSVLoader
from langchain.embeddings import OllamaEmbeddings
from langchain_iris import IRISVector

def get_insights(question, csv_file, iris_conn, collection_name):
    
    # Load and process the CSV data    
    loader = CSVLoader(csv_file)
    documents = loader.load()

    llm = Ollama(
        base_url="http://ollama:11434", 
        model="mistral", 
        temperature=0,
    )

    # Create embeddings
    embeddings = OllamaEmbeddings(model="mistral", base_url="http://ollama:11434", temperature=0)

    db = IRISVector.from_documents(
        embedding=embeddings, 
        documents=documents,
        connection_string=iris_conn,
        collection_name=collection_name,
        pre_delete_collection=True
    )

    qa = RetrievalQA.from_chain_type(
        llm=llm,
        chain_type="stuff",
        retriever=db.as_retriever())
    
    return qa({"query": question})

Sobre Streamlit:

O Streamlit é usada para desenvolver frontends utilizando a linguagem Python. Esta aplicação possui um chat em Streamlit para interagir com o Ollama, o LangChain e o IRIS, obtendo respostas relevantes:

import pandas as pd
import streamlit as st
from sqlalchemy import create_engine
import langchain_helper as lch

username = "_system"
password = "SYS"
hostname = "iris"
port = 51972
webport = 52773
namespace = "USER"
st.set_page_config(page_title="InterSystems IRIS Classes Demo", page_icon="📜")

st.title("Langchain IRIS Classes Chat")

with st.popover("Settings"):
    with st.spinner(text="Connecting to the IRIS classes"):
        engine = create_engine("iris://" + username + ":" + password + "@" + hostname + ":" + str(port) + "/" + namespace)
        connection  = engine.connect()
        query = 'select * from %Dictionary.ClassDefinition where substring(ID,1,1) <> \'%\' and  Copyright is null'
        df = pd.read_sql(query, con=connection)
        df.to_csv("classes.csv")
    
    username = st.text_input("Username:", username)
    password = st.text_input("Password:", password)
    hostname = st.text_input("Hostname:", hostname)
    port = int(st.text_input("Port:", port))
    webport = int(st.text_input("Web port:", webport))
    namespace = st.text_input("Namespace:", namespace)

            

# User query input
query = st.text_input(label="Enter your query")

# Submit button
if st.button(label="Ask IRIS Classes", type="primary"):
    
    with st.spinner(text="Generating response"):
        iris_conn_str = f"iris://{username}:{password}@{hostname}:{port}/{namespace}"
        response = lch.get_insights(query, "classes.csv", iris_conn=iris_conn_str, collection_name="classes")
        st.write(response['result'])
Discussion (0)1
Log in or sign up to continue