ADFSでSQL Server以外のDBから属性を取得する場合


1.はじめに

以前、SAMLの調査でいろいろ試したことをサルベージして記載しています。
ニッチ過ぎて使い道がほぼない。。。

2.DBから属性を取得する

  • SQL Serverから取得する場合は、要求規則にSQL文を設定すれば取得可能
  • SQL Server以外はC#で作成した自作のDLLが必要

3.C#のDLLソース

  • SQLはpostgresqlを使用
  • Visual Stadioのプロジェクトで下記のDLLを参照設定する(Microsoft.~のDLLはWindows Serverある)
    • Microsoft.IdentityModel
    • Microsoft.IdentityServer.ClaimsPolicy
    • Npgsql
using Npgsql;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using RemotingMessagingAsyncResult = System.Runtime.Remoting.Messaging.AsyncResult;
using Microsoft.IdentityModel.Threading;
using Microsoft.IdentityServer.ClaimsPolicy.Engine.AttributeStore;

namespace ClassLibrary1
{
    public class CustomAttributeStore : IAttributeStore
    {
        static readonly string EVENT_LOG_NAME = "CustomAttributeStore";
        static readonly string CONFIG_KEY01 = "CONNECTION_INFO";

        NpgsqlConnection _connection = null;

        string _connInfo = null;

        delegate string[][] RunQueryDelegate(string formatQuery);

        ~CustomAttributeStore()
        {
            //EventLog.WriteEntry(EVENT_LOG_NAME, "~CustomAttributeStore", EventLogEntryType.Information, 1, 100, null);
            // コネクションのクローズ
            _connection.Close();
        }

        public void Initialize(Dictionary<string, string> config)
        {
            /*
            if (!EventLog.SourceExists(EVENT_LOG_NAME))
            {
                EventLog.CreateEventSource(EVENT_LOG_NAME, "");
            }
            EventLog.WriteEntry(EVENT_LOG_NAME, "Initialize", EventLogEntryType.Information, 1, 100, null);
            */

            if (!config.TryGetValue(CONFIG_KEY01, out _connInfo))
            {
                throw new AttributeStoreInvalidConfigurationException("DB接続情報未設定");
            }

            // コネクションのオープン
            _connection = new NpgsqlConnection(_connInfo);
            _connection.Open();
        }

        public IAsyncResult BeginExecuteQuery(string query, string[] parameters, AsyncCallback callback, object state)
        {
            //EventLog.WriteEntry(EVENT_LOG_NAME, "BeginExecuteQuery", EventLogEntryType.Information, 1, 100, null);

            if (String.IsNullOrEmpty(query))
            {
                throw new AttributeStoreQueryFormatException("クエリ未設定");
            }

            RunQueryDelegate runQueryDelegate = new RunQueryDelegate(RunQuery);
            TypedAsyncResult<string[][]> asyncResult = new TypedAsyncResult<string[][]>(callback, state);
            runQueryDelegate.BeginInvoke(string.Format(query, parameters), new AsyncCallback(AsyncQueryCallback), asyncResult);
            return asyncResult;
        }

        public string[][] EndExecuteQuery(IAsyncResult result)
        {
            //EventLog.WriteEntry(EVENT_LOG_NAME, "EndExecuteQuery", EventLogEntryType.Information, 1, 100, null);
            return TypedAsyncResult<string[][]>.End(result);
        }

        private string[][] RunQuery(string formatQuery)
        {
            //EventLog.WriteEntry(EVENT_LOG_NAME, "RunQuery", EventLogEntryType.Information, 1, 100, null);
            //EventLog.WriteEntry(EVENT_LOG_NAME, formatQuery, EventLogEntryType.Information, 1, 100, null);

            // SQL実行&結果取得
            NpgsqlCommand command = new NpgsqlCommand(formatQuery, _connection);
            String data = null;
            using (NpgsqlDataReader reader = command.ExecuteReader())
            {
                if (reader.Read())
                {
                    data = reader[0].ToString();
                }
            }

            string[][] result = { new[] { data } };
            return result;
        }

        private void AsyncQueryCallback(IAsyncResult result)
        {
            //EventLog.WriteEntry(EVENT_LOG_NAME, "AsyncQueryCallback", EventLogEntryType.Information, 1, 100, null);

            TypedAsyncResult<string[][]> asyncResult = (TypedAsyncResult<string[][]>)result.AsyncState;
            RunQueryDelegate runQueryDelegate = (RunQueryDelegate)((RemotingMessagingAsyncResult)result).AsyncDelegate;

            string[][] values = null;
            Exception originalException = null;
            try
            {
                values = runQueryDelegate.EndInvoke(result);
            }
            catch (Exception e)
            {
                originalException = e;
            }
            asyncResult.Complete(values, false, originalException);
        }
    }
}

4.設定

  • 作成したDLLをWindows\ADFS直下に配置する。

- 下記を参考に、要求記述を追加する。

名称 設定値
表示名 返却ID
要求識別子 https://test.saml.local/returnid
  • 下記を参考に、カスタム属性ストアを追加する。
名称 設定値
表示名 test_Custom
カスタム属性ストアのクラス名 DLL名.クラス名,DLL名
オプションの初期化パラメータ 名前 CONNECTION_INFO
オプションの初期化パラメータ 値 Server=localhost;Port=5432;User Id=postgres;Password=password;Database=testdb
  • 下記を参考に、要求規則を追加する。
名称 設定値
要求規則名 test
カスタム規則 c:[Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"] => issue(store = "test_Custom", types = ("https://test.saml.local/returnid"), query = "select returnid1 from t_user where id = '{0}'", param = c.Value);
  • カスタム規則の設定値は下記のようになっています。
設定値 説明
c:[Type~] 指定したURIに対応する値がcに設定される
上記のURLはログイン名を指す
store 「属性ストア」の表示名を設定する
types 「要求記述」で追加したURIを設定する
このURIがsamlの属性名として返却される
query 実行するSQLを設定する
この取得値がsamlの属性値として返却される
param SQLのパラメータ
[c.Value]でcに設定された値を条件値として設定される