New post

Find

Article
· Dec 17, 2024 4m read

既存のセキュリティ設定をプログラムで変更する方法

こちらの記事では、既存のユーザ設定をプログラムで変更する方法をご紹介します。

ユーザロールを追加/削除したい、有効期限設定を変更したい、等の場合にお役立てください。

なお、ユーザ設定をプログラムで新規作成する方法は こちら の記事で紹介しております。
 


1.ある特定ユーザの設定を参照+変更する方法

2.既存の全てのユーザの設定を参照する方法

3.おまけ(Webアプリケーション情報の参照+変更)



1.ある特定ユーザの設定を参照+変更する方法

// ユーザを指定してロールを確認する(以下の例は、test ユーザ)
USER>zn "%SYS"
%SYS>do ##class(Security.Users).Get("test",.prop)

; 全プロパティ確認 
%SYS>zwrite prop       
prop("AccountNeverExpires")=0
prop("AutheEnabled")=0
prop("ChangePassword")=0
:
prop("Roles")="%DB_USER,%Developer,testrole"
; もしくは以下で直接取得
$SYS>write prop("Roles")
%DB_USER,%Developer,testrole

; ロールの追加をしたいとき
%SYS>write ##class(Security.Users).AddRoles("test","%SQL")  
1
%SYS>kill
%SYS>do ##class(Security.Users).Get("test",.prop)
 
%SYS>write prop("Roles")
%DB_USER,%Developer,%SQL,testrole

; Modify() メソッドでロールを変更することも可能
%SYS>set prop("Roles")="%Developer,testrole"
%SYS>write ##class(Security.Users).Modify("test",.prop)
1
%SYS>kill
%SYS>do ##class(Security.Users).Get("test",.prop)
 
%SYS>w prop("Roles")
%Developer,testrole

; ロールの削除をしたいとき(複数の時はカンマ区切り)
%SYS>write ##class(Security.Users).RemoveRoles("test","testrole")
1
%SYS>kill
%SYS>do ##class(Security.Users).Get("test",.prop)
 
%SYS>write prop("Roles")
%Developer
; 指定されたロール (またはロールのリスト) に特定のSQL特権を付与する場合
; 以下の例は、testroleロールに スキーマ=Sampleの、Insert,Update,Select,Delete権限を付与する
; 権限を付与したいスキーマ・テーブルのあるネームスペースで実行する
%SYS>zn "user"
USER>write $SYSTEM.SQL.Security.GrantPrivilege("Insert,Update,Select,Delete","Sample","Schema","testrole")
1


2.既存の全てのユーザの設定を参照する方法(例:ロール)

%SYS>set stmt=##class(%SQL.Statement).%New()
 
%SYS>write stmt.%PrepareClassQuery("Security.Users","List")
1
%SYS>set rs=stmt.%Execute()
 
%SYS>while rs.%Next() { write !,rs.%Get("Name")," || ",rs.%Get("Roles") }
 
Admin || %EnsRole_Administrator,%EnsRole_Developer,%Manager
CSPSystem ||
IAM || %IAM_API
SuperUser || %All
UnknownUser || %All
_Ensemble || %All
_PUBLIC ||
_SYSTEM || %All
test ||

※Listクエリの詳細情報はこちら、さらに詳しい情報が必要な場合は Detailクエリ を使用してください。


3.おまけ

Webアプリケーション情報(Security.Applications)や、ロールの情報(Security.Roles)等の、セキュリティ情報についても、同様の手順で参照・変更することが可能です。

*「RESTのディスパッチクラス」を参照・変更したいとき

// 既存のWebアプリケーション設定のディスパッチクラスを編集する場合
%SYS>do ##class(Security.Applications).Get("/csp/user/rest",.prop)
 
%SYS>zwrite prop
prop("AutheEnabled")=64
prop("AutoCompile")=1
prop("CSPZENEnabled")=1
prop("CSRFToken")=0
prop("ChangePasswordPage")=""
prop("CookiePath")="/csp/user/rest/"
prop("DeepSeeEnabled")=0
prop("Description")=""
prop("DispatchClass")="User.REST"%SYS>set Properties("DispatchClass")="User.REST2"   // ディスパッチクラスを変更
%SYS>write ##class(Security.Applications).Modify("/csp/user/rest",.Properties)
1


*「許可された認証方法」を参照・変更したいとき(例:認証なし+パスワード ⇒ パスワード)

%SYS>do ##class(Security.Applications).Get("/csp/user",.prop)
 
%SYS>zwrite prop
prop("AutheEnabled")=96           // 96 : 0110 0000(Bit 5 + Bit 6)
prop("AutoCompile")=1
prop("CSPZENEnabled")=1%SYS>set prop("AutheEnabled")=32    // 32 : 0010 0000 (Bit 5)
%SYS>write ##class(Security.Applications).Modify("/csp/user",.prop)
1
// AutheEnabled
// Bit 2 = AutheK5API
// Bit 4 = AutheOS
// Bit 5 - AuthePassword
// Bit 6 = AutheUnauthenticated
// Bit 11 = AutheLDAP
// Bit 13 = AutheDelegated
// Bit 14 = LoginToken
// Bit 20 = TwoFactorSMS
// Bit 21 = TwoFactorPW


詳細はクラスリファレンスをご覧ください。

Securityパッケージ

Discussion (0)0
Log in or sign up to continue
Question
· Dec 17, 2024

Converting iris stream to python bytes is very slow

I'm trying to use embedded python code that receives an Iris %Stream.GlobalBinary and uses image manipulation library PIL.

Because PIL can't work with IRIS %Stream, I need to convert the image to python bytes.

This process seems to have very bad performance compared to writing to a file and then loading it from a file in python.

Is there a better way to send a stream into python? The conversion code I'm using is below.

Thanks.

def iris_stream_to_bytes(stream):
	stream.Rewind()
	s = ""
	while not stream.AtEnd:
		r = stream.Read(1024)
		s += r
	b = bytearray()
	b.extend(map(ord, s))
	return b
5 Comments
Discussion (5)1
Log in or sign up to continue
InterSystems Official
· Dec 17, 2024

Publication de l'IPM 0.9.0

Nous avons publié IPM 0.9.0. J'ai déjà évoqué une partie de l'historique et du raisonnement ici ; pour résumer, il s'agit d'une version importante pour deux raisons : elle représente une réunification attendue depuis longtemps de notre travail interne et communautaire autour de la gestion des paquets ObjectScript centrée sur IRIS, et elle présente certaines incompatibilités rétroactives. Il existe plusieurs incompatibilités rétroactives nécessaires dans notre feuille de route, et nous les avons regroupées ; ce ne sera pas une nouvelle norme.

Sous le capot, la dénomination des classes et la structure des paquets ont complètement changé. Si vous utilisez directement des appels à (très probablement) %ZPM.PackageManager, la classe équivalente est désormais %IPM.Main. Pour les projets communautaires concernés par ce changement de nom, nous avons soumis un tas de demandes de participation pour mettre les choses à jour, et une certaine utilisation des anciens noms de classe %ZPM.* est migrée automatiquement lors de l'installation via ZPM pour permettre aux paquets de fonctionner avec les anciennes et les nouvelles versions d'IPM.

Si vous effectuez une mise à jour d'IPM 0.7.x vers 0.9.0, tout devrait « fonctionner » comme avant : il est disponible à l'échelle de l'instance, avec les données migrées vers de nouveaux emplacements de stockage.

Sur une nouvelle installation, les choses sont un peu différentes ; par défaut, le registre de communauté n'est pas activé et IPM n'est disponible que dans l'espace de noms dans lequel il a été installé. Pour obtenir un comportement équivalent à 0.7.x sur une nouvelle installation, vous devez exécuter les commandes suivantes pour mapper IPM partout et utiliser le registre de communauté par défaut, respectivement :

zpm "enable -map -globally"
zpm "repo -reset-defaults"

Un patch 0.9.1 sera bientôt disponible (aujourd'hui/demain, assez tôt pour que je me contente de mettre à jour ce message) et simplifiera ce processus dans une certaine mesure ; consultez https://github.com/intersystems/ipm/issues/662 pour le contexte/les mises à jour.

Si vous rencontrez des problèmes ou des questions, veuillez déposer un problème sur GitHub ; nous garderons également un œil sur la communauté des développeurs.

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

Table-valued function example: querying the Application Error Log

Let's start with a simple motivating question: over the past 14 days, what are my most common errors in the Application Error Log?

Answering this through the management portal or terminal is an annoying manual process - we should just be able to use SQL. Fortunately, there are a few class queries to help with this in the SYS.ApplicationError class in the %SYS namespace. You can answer the question for a single date with something like:

select "Error message",count(*)
from SYS.ApplicationError_ErrorList('CCR','12/16/2024')
group by "Error message"
order by 2 desc

Unfortunately, the structure of the class queries runs into the same overall structural limitations as the management portal pages; the ErrorList query needs a namespace and date. Surely there's a better approach than 14 union'ed calls to that class query for different dates, right? On some level, this is a real question; if there is a good way to do this through plain old SQL and I'm just missing it, please tell me!

The logical step is to write our own custom class query. This involves adding a Query class member (say <QueryName>) and implementing methods named <QueryName>Execute, <QueryName>Fetch, and <QueryName>Close. In general, Execute sets up the context for the class query and does any initial work, maintaining any state in qHandle; Fetch gets a single row and says whether we've found all the rows or not; and Close does any final cleanup. For example, if the implementation of Execute/Fetch uses a process-private global, Close might kill it.

Don't forget the magical [ SqlProc ] flag on the Query member that allows it to be called as a TVF (table-valued function) from other SQL queries!

Here's the full working example:

/// Utility queries to help access the application error log from SQL
Class AppS.Util.ApplicationErrorLog
{

/// Returns all application errors (all dates) from the application error log
Query All() As %Query(ROWSPEC = "Date:%Date,ErrorNumber:%Integer,ErrorMessage:%String,Username:%String") [ SqlProc ]
{
}

/// Gets a list of dates with errors and stashes it in qHandle
ClassMethod AllExecute(ByRef qHandle As %Binary) As %Status
{
    Set ns = $Namespace
    New $Namespace
    Set $Namespace = "%SYS"
    Set stmt = ##class(%SQL.Statement).%New()
    Set stmt.%SelectMode = 0
    Set result = ##class(%SQL.Statement).%ExecDirect(stmt,"select %DLIST(""Date"") ""Dates"" from SYS.ApplicationError_DateList(?)",ns)
    $$$ThrowSQLIfError(result.%SQLCODE,result.%Message)
    If 'result.%Next(.sc) {
        Return sc
    }
    Set qHandle("list") = result.%Get("Dates")
    Set qHandle("pointer") = 0
    Quit $$$OK
}

/// Gets the next row, advancing to the next date if needed
ClassMethod AllFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status [ PlaceAfter = AllExecute ]
{
    Set sc = $$$OK
    Set ns = $Namespace
    New $Namespace
    Set $Namespace = "%SYS"
    If $Get(qHandle("dateResult")) = "" {
        // Advance to the next date
        Set pointer = qHandle("pointer")
        If '$ListNext(qHandle("list"),pointer,oneDate) {
            Set AtEnd = 1
            Quit $$$OK
        }
        Set qHandle("pointer") = pointer
        Set qHandle("currentDate") = oneDate
        Set qHandle("dateResult") = ##class(%SQL.Statement).%ExecDirect(,"select * from SYS.ApplicationError_ErrorList(?,?)",ns,oneDate)
        $$$ThrowSQLIfError(qHandle("dateResult").%SQLCODE,qHandle("dateResult").%Message)
    }
    If qHandle("dateResult").%Next(.sc) {
        // If we have a row for the current date, add it
        Set Row = $ListBuild(qHandle("currentDate"),qHandle("dateResult").%GetData(1),qHandle("dateResult").%GetData(2),qHandle("dateResult").%GetData(6))
    } ElseIf $$$ISOK(sc) {
        // Otherwise, clear out the result set and call AllFetch to advance
        Set qHandle("dateResult") = ""
        Set $Namespace = ns
        Set sc = ..AllFetch(.qHandle,.Row,.AtEnd)
    }
    Quit sc
}

ClassMethod AllClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = AllExecute ]
{
    New $Namespace
    Set $Namespace = "%SYS"
    // This seems to be necessary sometimes to have %OnClose run properly
    Kill qHandle("dateResult")
    Quit $$$OK
}

}

In this example, we start out in a user namespace but all the queries really run in %SYS. Execute gets a list of error dates for the current namespace and stashes it in qHandle. Fetch advances to the next date when appropriate, then returns the next error for the current date. And Close makes sure that the class query goes out of scope in %SYS, because I sometimes got errors if it didn't. This was a little surprising but kind of makes sense because the class query we're calling only exists in %SYS.

There's a lot of power in reusability of table-valued functions. For example, we can add another one in the same class:

/// Gets a count by error message over the last <var>Days</var> days
Query ErrorCounts(Days As %Integer) As %SQLQuery(ROWSPEC = "Occurrences:%Integer,ErrorMessage:%String") [ SqlProc ]
{
    SELECT COUNT(*) AS Occurrences, ErrorMessage
    FROM AppS_Util.ApplicationErrorLog_All()
    WHERE DATEDIFF(D,"Date",$h) <= :Days
    GROUP BY ErrorMessage
    ORDER BY Occurrences DESC
}

And now getting our most common application errors over the last 14 days is as simple as:

call AppS_Util.ApplicationErrorLog_ErrorCounts(14)

Now we just need to fix them all! 😅

Discussion (0)1
Log in or sign up to continue
Article
· Dec 17, 2024 2m read

Editad vuestros Globals con VSCode y YAML

La mejor manera de listar, editar, guardar y eliminar globals es utilizando un IDE. Ahora es posible si usáis VSCode. También es posible guardar globals utilizando archivos YAML. Para ello deberéis seguir los siguientes pasos:

  1. Obtened una instancia de InterSystems IRIS e instalad la aplicación iris-global-yaml:
zpm:USER>install iris-global-yaml
  1. Si solo queréis probarlo en InterSystems IRIS, clonad el repositorio con git y ejecutadlo en Docker:
git clone https://github.com/yurimarx/iris-global-yaml.git
docker-compose up -d --build
  1. Id a https://openexchange.intersystems.com/package/IRIS-Global-VSCode-Editor, haced clic en el botón de GitHub, buscad el archivo iris-global-editor-0.0.1.vsix y guardadlo en vuestro disco local.
  2. Ahora, abrid vuestro IDE de VSCode y haced clic en extensiones:

 

  1. Haced clic en el botón ... y seleccionad Install from VSIX...:

 

  1. Seleccionad el archivo VSIX desde vuestro disco local para instalarlo (si la instalación falla, actualizad vuestro VSCode a la versión más reciente y volved a intentarlo):

  1. Id a View > Explorer:

  1. Cread o editad el archivo .vscode/settings.json con la configuración de conexiones (editadlo con vuestro host, puerto, namespace y credenciales):
"conf.irisGlobalEditor.serverconfig": { 
      "host": "http://localhost:52773", 
      "namespace": "USER", 
      "username": "_SYSTEM", 
      "password": "SYS"
}
  1. Id a la pestaña INTERSYSTEMS IRIS GLOBALS y haced clic en el botón refresh:

  1. VSCode listará todos los globals en el namespace configurado:

  1. En la parte superior de la pestaña, haced clic en el botón de más (+) para crear un nuevo global:

  1. Escribid el nombre del global y su valor, y pulsad enter:

  1. Haced clic en el botón de refrescar nuevamente y veréis vuestro nuevo global al final:

  1. Ahora, haced clic en el botón del editor (el último botón) para crear un archivo YAML y editar vuestro global:

  1. Se creará un nuevo archivo YAML con el contenido del global:

  1. Editad el archivo YAML para insertar subíndices en vuestro global (es muy importante usar la indentación con 4 espacios):
# IRIS-Global-YAML
USER:
 ^test:
     value: InterSystems IRIS
     subscripts:
        - ^test(1): 1
        - ^test(1,1): 1.1
        - ^test(1,2): 1.2
        - ^test(2,1): 2.1
        - ^test(2,2): 2.2
        - ^test(2,3): 2.3
        - ^test(2,4): 2.4
  1. Guardad el archivo en cualquier carpeta del proyecto y el contenido del global se guardará en el servidor IRIS:

  1. También podéis probar el botón de eliminar. ¡A divertirse!
Discussion (0)0
Log in or sign up to continue