拡張 SQL の実行ユーザと外部 DB 接続
拡張 SQL はプリザンターのデータベースに接続して実行されるため、DBMS の機能(SQL Server の Linked Server、PostgreSQL の Foreign Data Wrapper、MySQL の FEDERATED エンジン)を使えば、外部のデータベースも参照できます。このページでは、拡張 SQL がどの DB ユーザで実行されるか、そのユーザにどんな権限があるか、外部 DB に接続するために DBA 側で何を設定するかをまとめます。拡張 SQL そのものの設定項目は 拡張 SQL の活用 を参照してください。
INFO
実装の根拠は、確認時のソースへの固定リンクで示しています。外部 DB 接続の設定(Linked Server・FDW・FEDERATED)は各 DBMS の機能で、プリザンターのソースでは確かめていません。
拡張 SQL を実行する DB ユーザ
Rds.json には 3 種類の接続文字列があります。
| 接続文字列 | 使われる場面 |
|---|---|
SaConnectionString | CodeDefiner の実行時(データベースやユーザの作成) |
OwnerConnectionString | 削除済みレコードの復元(IssueUtilities.cs#L6097-L6100)など、本体の一部の処理 |
UserConnectionString | 画面・API の通常の読み書き。拡張 SQL も既定ではこれ |
拡張 SQL の DbUser に "Owner" を書くと OwnerConnectionString で実行されます。ただし DbUser を見ているのは API から呼び出す拡張 SQL(/api/extended/sql)の実行処理だけ です(ExtensionUtilities.cs#L89-L126)。確認したソースで DbUser を参照している箇所はここだけで、OnCreated などのイベントで動く拡張 SQL や Html: true の拡張 SQL は、DbUser を書いても UserConnectionString で実行されます。
switch (extendedSql.DbUser)
{
case "Owner":
connectionString = Parameters.Rds.OwnerConnectionString;
break;
default:
connectionString = null; // 既定の接続(UserConnectionString)
break;
}CodeDefiner が作る DB ユーザの権限
CodeDefiner はユーザ名が _Owner で終わるものをオーナー、それ以外を通常ユーザとして作ります(UsersConfigurator.cs#L60-L67)。付与される権限は DBMS ごとの SQL 定義(App_Data/Definitions/Sqls/<DBMS>/)で決まります。
| DBMS | オーナー | 通常ユーザ |
|---|---|---|
| SQL Server | プリザンターの DB で db_owner(CreateLoginAdmin.sql) | db_datareader と db_datawriter(GrantPrivilegeUser.sql) |
| PostgreSQL | ユーザ作成の SQL は空で、事前に作っておく | オーナーが所有するテーブルへの select, insert, update, delete(GrantPrivilegeUser.sql) |
| MySQL | プリザンターの DB への create, alter, index, drop と DML・ルーチン作成(with grant option) | プリザンターの DB への DML とルーチン作成(GrantPrivilegeUser.sql) |
SQL Server での違いを整理すると次のとおりです。
| 操作 | オーナー(db_owner) | 通常ユーザ(db_datareader / db_datawriter) |
|---|---|---|
| SELECT / INSERT / UPDATE / DELETE | ○ | ○ |
| DDL(CREATE / ALTER / DROP) | ○ | × |
| ストアドプロシージャの EXECUTE | ○ | 個別に付与が必要 |
CodeDefiner の権限はプリザンターの DB の中だけ
どの DBMS でも、CodeDefiner が付与するのはプリザンターのデータベース内の権限だけです。Linked Server のログインマッピング、FDW の USAGE 権限やユーザマッピング、FEDERATED テーブルの作成権限は含まれません。拡張 SQL から外部 DB を読むには、DBA が次の設定を別途行う必要があります。
外部データベースへの接続
| 項目 | SQL Server(Linked Server) | PostgreSQL(FDW) | MySQL(FEDERATED) |
|---|---|---|---|
| 設定の単位 | サーバー | サーバー+テーブル | テーブル |
| 接続できる相手 | OLE DB で接続できる DBMS | FDW 拡張次第(postgres_fdw、mysql_fdw、tds_fdw など) | MySQL のみ |
| クエリの書き方 | 4 部構成名か OPENQUERY | 通常の SELECT | 通常の SELECT |
| トランザクション | 分散トランザクション(MSDTC) | 対応 | 非対応(リモートの変更は即時確定) |
どの方式でも、拡張 SQL の接続に使う DB ユーザ(通常は UserConnectionString のユーザ、API 経由で DbUser: "Owner" ならオーナー)に対して、外部接続を使う権限を与えます。
SQL Server:Linked Server
Linked Server の作成・管理には sysadmin か setupadmin のサーバーロールが要ります。使う側に特定のロールは要らず、ログインマッピングで許可します。db_owner はデータベース内の権限なので、db_owner だけでは Linked Server は使えません。
-- リンクサーバーを作る
EXEC sp_addlinkedserver
@server = N'RemoteServer',
@srvproduct = N'',
@provider = N'SQLNCLI11',
@datasrc = N'remote-server-name';
-- プリザンターの接続ユーザをリモートのログインに対応付ける
EXEC sp_addlinkedsrvlogin
@rmtsrvname = N'RemoteServer',
@useself = N'False',
@locallogin = N'pleasanter_user',
@rmtuser = N'remote_user',
@rmtpassword = N'********';@locallogin = NULL にすると、個別の対応付けがないすべてのログインに適用されます。全ログインがリモートに接続できるようになるので、プリザンターの接続ユーザだけを個別に対応付けるほうが安全です。
{
"Name": "GetRemoteData",
"Api": true,
"CommandText": "SELECT * FROM OPENQUERY([RemoteServer], 'SELECT Code, Name FROM [RemoteDB].[dbo].[MasterTable]')"
}4 部構成名([RemoteServer].[RemoteDB].[dbo].[TableName])でも書けますが、WHERE 句での絞り込みをリモート側で行わせたいときは OPENQUERY を使います。
確認用の SQL とよくあるエラー
-- リンクサーバーの一覧
SELECT name, provider, data_source FROM sys.servers WHERE is_linked = 1;
-- ログインマッピングの一覧
SELECT s.name AS LinkedServerName, sp.name AS LocalLogin, ll.uses_self_credential, ll.remote_name
FROM sys.linked_logins ll
JOIN sys.servers s ON ll.server_id = s.server_id
LEFT JOIN sys.server_principals sp ON ll.local_principal_id = sp.principal_id
WHERE s.is_linked = 1;
-- 接続テスト
EXEC sp_testlinkedserver N'RemoteServer';
-- RPC を使う場合
EXEC sp_serveroption 'RemoteServer', 'rpc', 'true';
EXEC sp_serveroption 'RemoteServer', 'rpc out', 'true';| エラー | 原因 | 対処 |
|---|---|---|
| サーバーへのアクセスが拒否された、OLE DB プロバイダーでエラー | ログインマッピングがない | sp_addlinkedsrvlogin で対応付ける |
| リモート サーバーでログインに失敗した | リモート側の認証エラー | リモートのログイン名・パスワードを確認する |
| オブジェクトに対する SELECT 権限が拒否された | リモート DB 側の権限不足 | リモート側で権限を付与する |
| RPC 要求が無効 | RPC が無効 | sp_serveroption で有効にする |
書き込みは MSDTC が要ることがある
Linked Server への単純な SELECT や OPENQUERY の SELECT では MSDTC は要りません。一方、ローカルのトランザクションの中で Linked Server に書き込むと分散トランザクションになり、MSDTC(ネットワーク DTC アクセスの許可など)の設定が必要になります。OnCreated や OnUpdated の拡張 SQL から外部 DB へ書き込む場合は注意してください。リモートで完結させるなら EXEC ('...') AT [RemoteServer] を使う方法もあります。
PostgreSQL:Foreign Data Wrapper
CREATE EXTENSION IF NOT EXISTS postgres_fdw;
CREATE SERVER remote_server
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host 'remote-host', port '5432', dbname 'remote_db');
-- プリザンターの接続ユーザ用のユーザマッピング
CREATE USER MAPPING FOR pleasanter_user
SERVER remote_server
OPTIONS (user 'remote_user', password '********');
GRANT USAGE ON FOREIGN SERVER remote_server TO pleasanter_user;
-- 外部テーブル(またはスキーマごと IMPORT FOREIGN SCHEMA)
CREATE FOREIGN TABLE remote_table (
id integer,
name text
) SERVER remote_server
OPTIONS (schema_name 'public', table_name 'source_table');
GRANT SELECT ON remote_table TO pleasanter_user;| 操作 | 必要な権限 |
|---|---|
CREATE EXTENSION / CREATE SERVER | データベースの CREATE 権限またはスーパーユーザ |
| ユーザマッピングの作成 | 外部サーバーの USAGE(他ユーザ用はスーパーユーザ) |
| 外部テーブルの作成 | スキーマの CREATE |
| 外部テーブルの参照 | 外部テーブルへの SELECT など |
CodeDefiner の GrantPrivilegeUser.sql はオーナーが所有する通常テーブルにしか権限を付けないため、外部テーブルへの SELECT は別途付与します。権限は has_server_privilege('pleasanter_user', 'remote_server', 'USAGE') で確認できます。
MySQL:FEDERATED エンジン
FEDERATED エンジンが無効な場合は、my.cnf の [mysqld] に federated を書いて再起動します(SHOW ENGINES で確認)。
CREATE TABLE remote_table (
id INT NOT NULL,
name VARCHAR(100),
PRIMARY KEY (id)
) ENGINE=FEDERATED
CONNECTION='mysql://remote_user:********@remote-host:3306/remote_db/source_table';CREATE SERVER でサーバー定義を作る場合は SUPER 権限(MySQL 8.0.3 以降は FEDERATED_ADMIN でも可)が要ります。FEDERATED はトランザクションに対応せず、ローカルのインデックスも使われません。CONNECTION に書いたパスワードは平文で保存されます。