Nueva publicación

查找

Resumen
· 20 mayo, 2024

InterSystems 开发者社区中文版:每周摘要(2024年5月13日-19日)

Pregunta
· 20 mayo, 2024

ObjectScript: Cannot Read OpenId, Result Doesn't Display

Hello My Friends,

I have a problem with my script, In another code it works fine, but in this script It not display anything in InterSystems portal, when I try to debug, it gives the data, but why in portal it show nothing?

Can anyone help me please? Thank you

in Portal it show nothing

5 comentarios
Comentarios (5)3
Inicie sesión o regístrese para continuar
Artículo
· 19 mayo, 2024 Lectura de 10 min

Proteger los datos: Se un mejor cerrajero

Buenas a todos,

 

en capítulos anteriores, vimos como "Como controlar el acceso a tus recursos con OAuth2". En este primer artículo explicábamos como preparar un acceso seguro a nuestros recursos utilizando la potente herramienta que nos ofrece Intersytems del servidor de Autenticación. Al finalizar el mismo, comentábamos que si quisiéramos podríamos aplicar un control extra a este acceso y esto nos lleva a este artículo, por lo que seguiremos el siguiente índice:

1.- Introducción

   1.1..- De donde venimos

2.- Problema

   2.1.- ¿Cómo funciona la llave (Token)?

   2.2.- ¿Qué es una hoja de registro (LookUp Table)?

   2.3.- ¿Cómo construyo las cerraduras?

   2.4.- Cómo avisar a la policía de intrusos

 

1.- Introducción

Para empezar, situemos el problema a resolver:

Llevado a una situación más común imaginemos estar en una vivienda compartida, al cual cada inquilino accede al piso con la llave de la puerta de entrada, pero luego dentro de casa, cada habitación no tuviera llave, por lo que cualquier inquilino podría entrar en la habitación de cualquier otro y ¡no creemos que sea para dejar regalos!

Llegados a este punto, tendremos que poner cerraduras en todas las puertas y asegurar que cada inquilino solo pueda acceder a su habitación. ¡Saquemos al cerrajero que llevamos dentro!

Os dejo un pequeño video que de forma conceptual explica nuestro problema y el resultado de nuestra solución:

 

1.1.-De dónde venimos

Partimos de un servidor de autenticación (puerta de entrada) y un servidor de Recursos (la vivienda). En el servidor de autenticación vamos a tener 2 clientes dados de alta, cada uno con su usuario y su contraseña. Por otro lado, nuestro ESB que hace de servidor de recursos tendrá la labor de consultar los recursos determinados (habitaciones) para cada cliente. Por lo tanto, tenemos:

  • Cliente 1 (inquilino 1)
  • Cliente 2 (inquilino 2)
  • Recursos Cliente 1 (habitación de inquilino 1)
  • Recursos Cliente 2 (habitación de inquilino 2)

 

A continuación un diagrama de flujo del problema encontrado:

2.- Problema

Con la solución actual, nos encontramos con el problema de seguridad de datos, pues, si el cliente 2 pudiera conocer los datos de la petición del cliente 1, Con pasar su propia autorización (llave de la puerta de entrada) hacia los recursos, de la puerta hacia dentro, tendría libertad para acceder a los recursos (habitaciones) que quiera. El propietario de la vivienda nos ha pedido que pongamos una cerradura en cada puerta, así que vamos con ello.

A continuación, se indican los pasos a seguir para ser mejores cerrajeros.

2.1.- ¿Cómo funciona la llave (Token)?

            El Servidor de Autenticación, una vez recibe un usuario y una contraseña válidos, nos devolverá un token con el siguiente formato:

{

    "access_token""eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJqdGkiOiJodHRwczovL2F1dGhzcnZyLnNjcy1zYWx1ZC5vcmc6NTc3NzYvb2F1dGgyLmdqQnZqR01JQzJqeVBYZW12dW5EWW1vTnJnMCIsImlzcyI6Imh0dHBzOi8vYXV0aHNydnIuc2NzLXNhbHVkLm9yZzo1Nzc3Ni9vYXV0aDIiLCJzdWIiOiJ5WUZIZElGWDQyZEVEV3FhZHhzWFJxbmZtZW1IVEVZYVFlQ09JbWRKVXpRIiwiZXhwIjoxNzE2MTU0Mjk4LCJhdWQiOiJ5WUZIZElGWDQyZEVEV3FhZHhzWFJxbmZtZW1IVEVZYVFlQ09JbWRKVXpRIn0.",

    "token_type""bearer",

    "expires_in"60,

    "scope""my/scope"
}

Este “Access_token” que viene en base64 se compone de información interesante para nuestro objetivo. Sabiendo que este token se genera por JWT, una vez tratado, vemos que se compone de:

  • Header
  • Payload

Header

Se compone de:

{

  "typ": "JWT",

  "alg": "none"
}

 

Payload

Se compone de:

{

  "jti": "https://authsrvr.org:57775/oauth2.gjBvjGMIC2jyPXemvunDYmoNrg0",

  "iss": "https://authsrvr.org:57775/oauth2",

  "sub": "yYFHdIFX42dEDWqadxsXRqnfmemHTEYaQeCOImdJUzQ",

  "exp": 1716154298,

  "aud": "yYFHdIFX42dEDWqadxsXRqnfmemHTEYaQeCOImdJUzQ"
}

 

Del payload el dato que nos vamos a quedar es el “aud”, ya que este dato es el usuario registrado en el servidor de autenticación que hemos creado previamente.

Para más información sobre este contenido, podéis consultar la documentación oficial de IS: (https://docs.intersystems.com/irisforhealthlatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SYS.OAuth2.Validation)

 

2.2.- ¿Qué es una hoja de registro (LookUp Table)?

No profundizaremos en este momento en las LoopUp Tables, lo que, si nos interesa saber en este punto, es que son unas tablas internas de cada namespace, en las cuales podemos guardar datos en el formato “clave/valor”.

Para más información sobre este contenido, podéis consultar la documentación oficial de IS:

https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=ECONFIG_other_lookup_tables

 

2.3.- ¿Cómo construyo las cerraduras?

En este punto juntemos los 2 puntos anteriores, es decir, con el escenario actual, tenemos:

                -              Un dato del Token: “aud”

                -              Una tabla interna que almacena datos: “clave/valor”

Como responsables de la seguridad, sabemos que a este cliente solo hay que dejarle acceder a sus propios recursos, por lo que para ello, en la LookUp table, vamos a guardar:

-              Clave: el “aud”

-              Valor: el condicionante de acceso a los recursos. En este ejemplo pongamos “1”.

 

Ahora que ya tenemos preparados los "materiales", nos toca construir la cerradura. Para ello explicamos a continuación los pasos a Seguir:

2.3.1.-  Recepción del Token

2.3.2.-  Extracción y validación del “aud”

 

2.3.1.-  Recepción del Token

Partimos para el ejemplo de un servicio REST que tendrá los siguientes parámetros:

Method MISERVICIO(pInput As %Stream.Object, Output pOutput As %Stream.Object, indicadorRecursos As %String(MAXLEN=""))As %Status

La información que vamos a requerir viene en el pInput y el indicadorRecursos. En el inicio de nuestro servicio observemos las siguientes líneas y lo que hace cada una:

{
    //Necesitamos sacar la cabecera "Authorization" de la petición REST
    set authorization = $tr(pInput.GetAttribute("authorization"),"")

    //Necesitamos sacar Token
    set token = $PIECE(authorization,"Bearer ",2)

    //Necesitamos validar el Token. Este apartado lo vamos a obviar en esta ocasión.
    set respuesta = ..ResServer(token)
    if (respuesta = 1){ Avanzar en el servicio } else{ Devolver error }

    //Necesitamos validar el “aud”
    set audValidado = ..ValidarUsernameOAUTH2(token,indicadorRecursos,.pOutput)

    //Si el “aud” es válido, podemos avanzar, sino salimos con el mensaje de respuesta que configuremos
    if (audValidado = 0)
    {
        quit ERROR            
    }
}

Como podemos ver, en la línea de validación del "aud" nos encontramos con la llamada a un método (ValidarUsernameOAUTH2) el cual os expongo a continuación.

 

2.3.2.-  Extracción del “aud”

En el siguiente método podemos ver paso a paso como se extrae y valida el "aud".

ClassMethod ValidarUsernameOAUTH2(token As %String(MAXLEN=""), indicadorRecursos As %String(MAXLEN=""), Output pOutput As %Stream.Object) As %Boolean
{
 set claseAux = ##class(%ZEN.Auxiliary.jsonProvider).%New()
   
 // Buscamos el cliente de recursos
 Set client = ##class(OAuth2.Client).Open("resserver",.sc)
 If client  = "" Quit      
 // Inicializamos nuestra referencia para acceder a los recursos, pues si no cambia de valor, nunca podremos acceder a los recursos
 set codigoRecursosOAUTH2 = ""
 try{
  // Convertir JWT a objeto 
  set sc = ..JWTToObject(client,token,.securityParameters,.jsonObject)

  // Extraemos el “aud” del JSON que generamos al convertir el Token
  set usuarioOAUTH2 = jsonObject.aud

  // Extraemos de la LookUpTable que creamos en el paso anterior, el Valor que nos da el usuario que hemos extraído del Token. Si este valor es “”, quiere decir que no existe el usuario. Si existe, extraemos el Valor. Este valor es el que tendremos que enfrentar con el indicadorRecursos que tenemos en la entrada. Pues si estos no coinciden, querrá decir que el usuario está intentando acceder a recursos a los cuales no le hemos autorizado acceder.
  set codigoRecursosOAUTH2 = ##class(Util.TablasMaestras).getValorMaestra("SERVIDORAUTH.VALIDACION",usuarioOAUTH2)

 }catch{
  // Si hay errores, devolvemos una respuesta de error de acceso
  set response             = ##class(Mensajes.Response.Autorización.DatosResponse).%New()
  set response.codigo      = "-4"
  set response.descripcion = “Error de acceso"

  set tSC = claseAux.%WriteJSONStreamFromObject(.pOutput,.response,,,,"aeloqtuw")
   
  // Enviamos el JSON con cabeceras
  Do:$$$ISOK(tSC) pOutput.SetAttribute("Content-Type","application/json")
  do pOutput.SetAttribute("Access-Control-Allow-Origin","*")
  do pOutput.SetAttribute("Access-Control-Allow-Credentials","true")
  do pOutput.SetAttribute("Access-Control-Allow-Methods","GET")
  do pOutput.SetAttribute("Access-Control-Allow-Headers","request,Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers")
 }

 // Si hubo error, significa que el token es incorrecto. Por lo tanto devolvemos un 0
 if (codigoRecursosOAUTH2 = ""){
    Quit '$$$OK                           
 }
 // Si la consulta a la LoopUp Table nos devolvió datos, debemos hacer ahora la validación de este valor.
 if (codigoRecursosOAUTH2 '= ""){
    // Si son distintos, significa que el aud NO es de estos recursos
    if (codigoRecursosOAUTH2 '= indicadorRecursos){
     set response             = ##class(Mensajes.Response.Autorización.DatosResponse).%New()
     set response.codigo      = "-3"
     set response.descripcion = “Consulta no Autorizada"
     set tSC = claseAux.%WriteJSONStreamFromObject(.pOutput,.response,,,,"aeloqtuw")
    
     // Enviamos el JSON con cabeceras
     Do:$$$ISOK(tSC) pOutput.SetAttribute("Content-Type","application/json")
     do pOutput.SetAttribute("Access-Control-Allow-Origin","*")
     do pOutput.SetAttribute("Access-Control-Allow-Credentials","true")
     do pOutput.SetAttribute("Access-Control-Allow-Methods","GET")
     do pOutput.SetAttribute("Access-Control-Allow-Headers","request,Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers")

  // Si son distintos, el método responde un 0
     Quit '$$$OK                                                          
    }
 }

 // Si son iguales, el método responde un 1
 quit $$$OK
}

Aclarando este apartado:

Supongamos que el indicadorRecursos de entrada viene a "2". Cuando vayamos a comparar el valor obtenido de la LoopUpTable ("1") con este valor, no coincidirán, por lo que no será posible acceder a los recursos. En cambio, si este valor de entrada es "1", si coincidirá y podrá acceder a los mismos.

Llegados a este punto, ya habríamos aplicado la cerradura (seguridad) a nuestra primera habitación (recursos). Para el resto de habitaciones, bastaría con repetir los mismos pasos para el resto de clientes/recursos.

2.4.- Cómo avisar a la policía de intrusos

Una vez detectado quien puede acceder o no a los recursos, podemos plantearnos monitorizar si alguien se esta pasando de listillo intentando acceder a recursos que no le corresponden. Para ello, podemos hacer uso de las herramientas de Intersystems para el envío de emails, notificaciones push, o incluso alimentar cuadros de mando y tener un mayor control de estos accesos incorrectos. Estas alertas deben configurarse en el momento que vamos a devolver un "0", pues es el momento donde tenemos los datos de acceso así como la respuesta del acceso no permitido.

 

La redacción de este artículo se ha planteado de forma esquematizada para que pueda ser adaptada a cualquier aplicación que puedan plantear. Se ha dejado indicado el código mínimo para que puedan entender y reutilizar de forma lo mas práctica posible. Os prometo que si seguís las indicaciones paso a paso para entender el proceso, ¡empezareis a poner cerraduras por doquier!

Finalmente espero que les sea de utilidad, ¡y que se conviertan en mejores cerrajeros!

Muchas gracias por el tiempo que han dedicado a esta lectura.

PD:Por favor, no duden en plantear cuestiones que ayuden a mejorar la redacción y explicación del mismo por si algo no ha quedado correctamente entendido.

3 comentarios
Comentarios (3)3
Inicie sesión o regístrese para continuar
Artículo
· 19 mayo, 2024 Lectura de 7 min

IRIS BI開発者向けチュートリアルを試してみる(10)

はじめに

IRIS BIチュートリアル試してみたシリーズの10回目です。
今回はチュートリアル最後のページ「ピボット・テーブルおよびダッシュボードの作成とパッケージ化」についてです。
これまでアーキテクトとアナライザを使用してきましたが、今回はユーザポータル画面を使ってダッシュボードを作成していきます。
では、早速はじめていきましょう。

ピボット・テーブルの作成

ダッシュボードはピボットテーブルをソースデータとして作成することができます。ですので、まずはソースとなるピボットテーブルを作成します。
アナライザ画面を開き、Tutorial キューブを開きます。
AgeD ディメンジョン内の Age Group レベルを [行] にドラッグ&ドロップし、続いて Age Bucket レベルを Age Group レベルの矢印記号 ↘ のところにドラッグ&ドロップします。
 
そうしますと、Age Group レベルの隣に Age Bucket レベルが表示されます。[行] の表示も、Age Bucket レベルが一段下げられた形で表示されています。

※注意:サンプルデータはランダムに作成されるため、こちらの画面表示とみなさまの実行結果は一致しないことがあります。
 
さらに Count メジャーを [列] に追加し、All Patients メンバを [行] に追加します。
All Patients をドラッグ&ドロップする場所ですが、2つある「行をここにドロップ」のうちの下の方になります。Age Group と同じレベルになるところです。
 
そうしますと、以下のようなピボットテーブルができあがります。

 
では、このピボットテーブルに名前を付けて保存します。画面上部の [保存] ボタンをクリックしてダイアログを開きます。
ダイアログにて、[フォルダ] に Tutorial 、[ピボット名] に Patients by Age Group と入力して保存します。
 
保存のメッセージが表示されたら完了です。

 
では、もう1つピボットテーブルを作ります。[新規] ボタンで新しい画面を開き、以下のように設定します。

  • 行:Diagnoses レベル(DiagD ディメンジョン)
  • 列:Count メジャー、Avg Age メジャー

以下のような感じのピボットテーブルになります。
 
こちらも名前を付けて保存します。[フォルダ] は Tutorial 、[ピボット名] は Patients by Diagnosis とします。
 
これでダッシュボード作成に必要なアイテムが揃いました。

ダッシュボードの作成

ダッシュボードの作成はユーザーポータル画面から行います。管理ポータルより Analytics → ユーザポータル と選択します。
 
以下のような画面が表示されます。既にいくつかのダッシュボードがありますね。

これらは1回目の記事でチュートリアルの準備作業を実施したときに作成されたものです。ダッシュボード作成時の参考になりますので、お時間のある時にのぞいてみてください。
では、新しいダッシュボードを作成します。画面左上の [メニュー] リンクをクリックし、 [新規ダッシュボード] を選択します。
 
ダイアログが開いたら、[フォルダ] に Tutorial を入力(またはドロップダウンリストから選択)し、[ダッシュボード名] に Sample Dashboard と入力して [OK] をクリックします。
 
そうしますと、空のダッシュボード画面が表示されます。
ダッシュボード・エディターのメニューを表示するには、画面左端にある [>] のところをクリックします。 
 
ダッシュボード・エディターのメニューが出てきました。まずウィジェットの設定を行いますので、[ウィジェット] をクリックします。

 
ウィジェットのメニューが表示されます。ここではダッシュボードに使われているウィジェットのリストが表示されますが、作成したばかりですので今はなにもありません。
画面上部の [+] をクリックしてウィジェットの追加設定をしていきます。

 
ウィジェット・ウィザードのダイアログが開きます。左側のメニューから [ピボットとグラフ] をクリックします。
 
展開されたメニューから、[テーブル] を選択します。

 
データソースの指定を行います。虫眼鏡アイコンをクリックします。

 
選択ダイアログから Tutorial → Patients by Age Group と選択します。

 
データソースが設定されます。[OK] で完了します。

 
もし、以下のダイアログが表示されたら、[Reload] をクリックします。

 
先ほど作成したピボットテーブルが画面に表示されました。これで Patients by Age Group の追加は完了です。

 
続いて、もう1つのピボットテーブル Patients by Diagnosis も追加します。先ほどと同様の手順ですが、ピボットテーブルの選択のみ異なります。

 
Patients by Diagnosis ピボットテーブルの追加もできました。が、画面では Patients by Diagnosis しか表示されていません。

 
これは、先に設定した Patients by Age Group の上にかぶさるように Patients by Diagnosis が表示されているためです。
Patients by Diagnosis の上部(メニューバー)にマウスカーソルを合わせるとアイコンが上下左右の矢印に変わりますので、その場所でドラッグし、ピボットテーブル全体を下の方にずらしていきます。
 
これで、隠れていた Patients by Age Group も表示されました。

 
また、各ウィジェットの右下のところをドラッグしながら、ウィジェットのサイズを変えることができます。
 
では、この状態でダッシュボードを保存します。画面上部の [保存] リンクをクリックします。
 
保存されました、というダイアログ等の表示はありません。ただし、先ほどまで表示されていた「変更されました」が消えました。未保存の変更が無くなったということです。
 

続いては、このダッシュボードにフィルタのコントロールを追加します。

ダッシュボード・エディターを開き、ウィジェットの一覧から ウィジェット1(Patients by Age Group)を選択します。
 
ウィジェット1のメニューから [コントロール] メニューをクリックします。
 
コントロールの追加を行いますので、画面上部の [+] をクリックします。
 
コントロール・ウィザードのダイアログが表示されますので、以下のように設定します。ドロップダウンリストから選択可能なものは、選択して設定します。

  • 場所:ダッシュボード
  • ターゲット:* 
  • アクション:フィルタを適用
  • フィルタ:ZIP Code
  • ラベルまたはアイコンを制御:ZIP Code

 
[OK] で設定を完了すると、ダッシュボード画面にフィルタコントロールが追加されます。
 
同様に Allergies と Favorite Color のフィルタを追加します。コントロール上部の [+] から足していきます。

 
 
3つのフィルタコントロールを追加した画面は、以下のようになります。
 

作成した各々のフィルタに規定値を設定します。先ほどのコントロールのメニューまで進み、ZIP Code をクリックします。
 
ZIP Code のコントロール設定メニューが表示されました。この中の [デフォルト値] の虫眼鏡アイコンをクリックします。
 
デフォルト値エディタのダイアログが開きます。[フィルタの値を選択] の横にある虫眼鏡アイコンをクリックします。

 
ZIP Code のメンバ値が一覧表示されます。今回は 32007 を既定値としますので、32007 にチェックを入れ、上部の ✓ アイコンをクリックします。

 
32007の値が設定できたら、[OK] で完了します。

 
もし、以下のダイアログが表示されたら [Leave] をクリックします。

 
ZIP Code の既定値に 32007 が設定されました。また、ピボットテーブルも、その条件によりフィルタされた値に変わりました。

 
同様に、Allergies フィルタには既定値として「soy」を、Favorite Color フィルタには既定値として「Blue」を設定します。
ダッシュボード・エディターのメニューを最初からたどるときは、ウィジェット → ウィジェット1 → コントロール → 設定したいフィルタコントロール と選んでいきます。
最終的な姿は、以下のようになりました。

 
当然、ダッシュボードの上からフィルタ条件の変更は可能です。例えば ZIP Code のフィルタ条件を 32007→34577 に変えてみます。
 
ピボットテーブルの結果が変わったことが分かります。
 

おわりに

今回は、アナライザ画面で作成したピボットテーブルを基に、簡単なダッシュボードを作成しました。また、2つのピボットテーブルに対するフィルタコントロールも追加しました。
今回はピボットテーブルだけでしたが、ウィジェットには各種グラフやスコアカードなど、多様なコントロールが用意されています。それらをうまく組み合わせて、より分かりやすいダッシュボードを作成することが可能です。
次回は、いよいよ最終回となります。最後のトピック「ピボット・テーブルおよびダッシュボードのエクスポートとパッケージ化」について触れます。お楽しみに!

Comentarios (0)1
Inicie sesión o regístrese para continuar