Oracle® Fusion Middleware Oracle Access Management開発者ガイド 11g リリース2 (11.1.2.2.0) for All Platforms B69537-08 |
|
前 |
次 |
本章では、プラグイン・フレームワークを使用してOracle Access Management OAuthサーバーをカスタマイズする方法について説明します。この章の内容は次のとおりです。
Oracle Access Management OAuthサーバーには、Javaインタフェースを使用して次のような機能をカスタマイズおよび拡張できるプラグイン・フレームワークが使用されています。
クライアント管理 - このプラグインは、クライアント認証、認可およびプロファイル管理を外部モジュールに委任します。
リソース・サーバー・プロファイル管理 - このプラグインは、リソース・サーバー認証、認可およびプロファイル管理を外部モジュールに委任します。
トークン属性 - 管理者はこのプラグインを使用して生成されるアクセス・トークンにカスタム要求を追加できます。
認可および承認サービス - このプラグインは、ユーザー承認ベースの認可の間の認可義務を取り扱います。
アダプティブ・アクセス・セキュリティ - このプラグインは、Oracle Adaptive Access Managerなどのアダプティブ・アクセス・セキュリティ・アプリケーションをOAuthサービスと統合します。
クライアント管理プラグインは、次のクライアント機能を外部モジュールに委任します。
クライアント認証
クライアント認可(権限の評価)
クライアント・プロファイル管理(登録および読込みの両方)
管理者は、OAuthサービスがOAuthクライアントからの認可リクエストを処理する前にOAuthクライアント・プロファイルを構成する必要があります。リクエストが送信されると、クライアント・プラグインはクライアントがどの権限付与タイプやスコープなどにアクセスできるかを評価します。
OAuth開発者はカスタム・プラグインの実装を記述して、たとえばカスタムのクライアント認証メカニズムを作成する、OAuthクライアント・プロファイルをデフォルトのクライアントMBeanリポジトリではなく外部リポジトリ格納するなどのカスタム要求を処理できます。クライアント管理プラグインは、次の3つのJavaインタフェースから構成されています。
クライアント認証インタフェース - OAuthクライアントの資格証明およびリダイレクトURIを検証します。
クライアント権限インタフェース - OAuthクライアントがあるOAuth権限付与タイプおよびスコープを使用するために必要な権限を持っているかどうかを評価します。また、要求されたスコープに必要なユーザー承認も検証します。OAuthサービス・フレームワークは、クライアント権限のチェックの際にクライアントのIPアドレスとともに要求されたパラメータをプラグインに送信します。
動的クライアント登録インタフェース - OAuthクライアント・プロファイルをクライアント・リポジトリに書き込み、必要に応じてプロファイルを取得します。このインタフェースは、クライアント・プロファイル読込み部およびクライアント・プロファイル書込み部の2つの部分で構成されています。このインタフェースは、OAuthサービス・ランタイムによって実行されます。
デフォルトの実装には、クライアント・プロファイル読込み、クライアント認証およびクライアント権限のインタフェースが含まれています。IDM OAuthサービスは、実行時にデフォルトのプラグインの実装を呼び出し、プラグインはOAuth MBeanリポジトリ・サーバーを使用してクライアント権限の読込み、認証および評価を行います。OAuth MBeanコンソールおよびWLSTコマンド行は、MBean APIを使用してOAuth MBeanリポジトリからOAuthクライアント・プロファイルの読込みおよび書込みを行います。
クライアント・アプリケーションは、認可リクエストの一部としてclient_id
パラメータを、トークン・エンドポイント・リクエスト内の認可ヘッダーとしてclient_id+client_secret
のBase64値を送信します。
プラグインは、クライアント・リポジトリでクライアントIDおよびシークレット値を検証します。
プラグインは検証の際に、クライアントIDとoauth.xml
(またはLDAPおよび他のリポジトリの場合もあります)に格納されているクライアント定義を比較します。シークレットはCSFで検証されます。
認可リクエストのclient_id
の値がinvalidだった場合、認可エンドポイントはinvalid_client
エラーを返します。トークン・エンドポイントは、Base64認可ヘッダーの値を検証します。
クライアントの資格証明が有効である場合、資格証明は認可コードおよびアクセス・トークンとともにレスポンスに埋め込まれます。たとえば、発行されたJWT認可コードおよびアクセス・トークンには次の要求が含まれます。
"oracle.oauth.client_origin_id":"2a180cbc780742698cf51d9a19f80ff6"
プラグインは、クライアントが構成されたスコープをリクエストしているかどうかを確認します。そうでない場合は、プラグインはリクエストを拒否してエラー・レスポンスを返します。
プラグインは、クライアントが構成された権限付与フローをリクエストしているかどうかを確認します。そうでない場合は、プラグインはリクエストを拒否してエラー・レスポンスを返します。
次の例では、認可コードのリクエストで使用されるclient_id
およびclient_secret
属性が示されています。
GET /authorize?response_type=code &client_id=s6BhdRkqt3&state=xyz &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Freturn &scope=user_read
次の例では、Basic認証ヘッダーで送信されるクライアントIDおよびシークレットが示されています。
POST /token HTTP/1.1 Host: server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded;charset=UTF-8 grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
カスタム・プラグインをデプロイする際には、次の注意点を参照してください。
プラグインをデプロイするには、JARファイルを次の場所にコピーします。
$ORACLE_HOME/user_projects/domains/base_domain/config/fmwconfig/oic/plugins/
カスタム・プラグインの開発にサード・パーティ製のライブラリが使用された場合、コンテナ(WebLogicまたはWebSphere)のクラスパスでライブラリが使用可能である必要があります。
JARファイルのデプロイ後に、管理対象サーバーを再起動します。
OAMコンソールを使用して新しいクライアント・セキュリティ・プラグインを作成します。
OAMコンソールにログインします。
「起動パッド」から、「OAuthサービス」→対象のドメイン→「OAuthプラグイン」→「クライアント・プラグイン」を選択します。
新しいプラグインを作成します。詳細は、次のスクリーン・キャプチャを参照してください。
「起動パッド」から、「OAuthサービス」→対象のドメイン→「OAuthサービス・プロファイル」→OAuthサービス・プロファイルを選択します。
「プラグイン」セクションでメニューからクライアント・プラグインを選択し、サービス・プロファイルに割り当てます。
package mypackage; import java.util.ArrayList; import oracle.security.idaas.oauth.client.ClientAuthenticationResponse; import oracle.security.idaas.oauth.client.ClientAuthorizationResponse; import oracle.security.idaas.oauth.client.ClientProfile; import oracle.security.idaas.oauth.client.ClientRequest; import oracle.security.idaas.oauth.client.ClientScopeProfile; import oracle.security.idaas.oauth.client.ClientSecurityManager; import oracle.security.idaas.oauth.client.ClientSecurityManagerException; import oracle.security.idaas.oauth.client.ClientWritableProfile; import oracle.security.idaas.oauth.common.appinfra.AppAuthnRequest; import oracle.security.idaas.oauth.common.provider.exception.OAuthMisconfigurationException; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.atomic.AtomicLong; import oracle.security.idaas.oauth.client.ClientScopeWritableProfile; import oracle.security.idaas.oauth.client.impl.ClientAuthenticationResponseImpl; import oracle.security.idaas.oauth.client.impl.ClientAuthorizationResponseImpl; import oracle.security.idaas.oauth.client.impl.ClientScopeWritableProfileImpl; import oracle.security.idaas.oauth.client.impl.ClientWritableProfileImpl; import oracle.security.idaas.oauth.common.Validator; import oracle.security.idaas.oauth.common.appinfra.AppWritableProfile; import oracle.security.idaas.oauth.common.appinfra.impl.AppWritableProfileImpl; public class CustomClientSecurityPlugin implements ClientSecurityManager{ protected static AtomicLong sequenceNumberGenerator = new AtomicLong(0); protected ReadWriteLock lock = new ReentrantReadWriteLock(); public CustomClientSecurityPlugin() { super(); } public void init(Map<String, Object> config) throws OAuthMisconfigurationException { System.out.println("CustomClientSecurityPlugin::init()"); } @Override public ClientProfile readClientProfile(ClientRequest clientRequest) throws ClientSecurityManagerException { System.out.println("CustomClientSecurityPlugin::readClientProfile()"); ClientProfile clientProfile = getClientProfile( clientRequest.getAppId() ); if ( clientProfile != null && clientRequest.getAppId().equals( clientProfile.getAppId()) ) return clientProfile; else { System.out.println("CustomClientSecurityPlugin::readClientProfile(): No Client Profile found."); return null; } } public boolean isValidConfidentialSecret(AppAuthnRequest appAuthnRequest) { System.out.println("CustomClientSecurityPlugin::isValidConfidentialSecret()"); boolean result = false; ClientProfile clientProfile = getClientProfile(appAuthnRequest.getAppId()); if ( clientProfile != null && compareChars(appAuthnRequest.getSecret(), clientProfile.getAppSecret().getSecret()) ) result = true; return result; } public boolean isValidRedirectURI(ClientRequest clientRequest) { System.out.println("CustomClientSecurityPlugin::isValidRedirectURI()"); boolean result = false; return result; } @Override public boolean isUserConsentRequired(ClientRequest clientRequest) { System.out.println("CustomClientSecurityPlugin::isValidRedirectURI()"); boolean result = false; ClientProfile clientProfile = getClientProfile(clientRequest.getAppId()); if( clientProfile != null ) result = clientProfile.getClientScopeProfile().isUserConsentRequired(); return result; } @Override public void destroy() { } @Override public Collection<ClientProfile> readClientProfiles(ClientRequest clientRequest) throws ClientSecurityManagerException { System.out.println("CustomClientSecurityPlugin::readClientProfiles()"); Collection<ClientProfile> clientProfiles = new HashSet<ClientProfile>(); return clientProfiles; } @Override public ClientProfile write(ClientWritableProfile clientWritableProfile) throws ClientSecurityManagerException { throw new UnsupportedOperationException(); } @Override public ClientProfile update(ClientWritableProfile clientWritableProfile) throws ClientSecurityManagerException { throw new UnsupportedOperationException(); } @Override public void delete(ClientWritableProfile clientWritableProfile) throws ClientSecurityManagerException { throw new UnsupportedOperationException(); } /** * This methods does perform client authentication against XML repository * This method returns only authentication result (true/false) to the IDM OAuth framework in R2 PS2. * In future, this method may send error message if authentication is failed and other additional attributes * which may be used for client authorization and authorization plugin * @param clientRequest contains requested client id, secret, ip address and requested map * @return ClientAuthenticationResponse contains authentication result, error message and additional attributes * @throws ClientSecurityManagerException */ @Override public ClientAuthenticationResponse authenticate(ClientRequest clientRequest) throws ClientSecurityManagerException { ClientAuthenticationResponse clientAuthenticationResponse = new ClientAuthenticationResponseImpl(); clientAuthenticationResponse.setResult(isValidConfidentialSecret(clientRequest)); return clientAuthenticationResponse; } @Override public ClientAuthorizationResponse isAuthorized(ClientRequest clientRequest) throws ClientSecurityManagerException { ClientAuthorizationResponse clientAuthorizationResponse = new ClientAuthorizationResponseImpl(); if (allowToUseScopes(clientRequest) && allowToUseGrantTypes(clientRequest)) { clientAuthorizationResponse.setResult(true); } else { clientAuthorizationResponse.setResult(false); } return clientAuthorizationResponse; } private ClientProfile getClientProfile(String appid) { List<ClientProfile> CProfiles = getClientProfiles(); for (ClientProfile profile: CProfiles) { if (appid.equals( profile.getAppId())) return profile; } return null; } private List<ClientProfile> getClientProfiles() { List<ClientProfile> CProfiles = new ArrayList<ClientProfile>(); ClientWritableProfile clientWritableProfile = new ClientWritableProfileImpl(); List<String> grantTypes = new ArrayList<String>(); grantTypes.add( Validator.OAUTH_STD_GRANT_TYPE_CLIENT_CRED ); grantTypes.add( Validator.OAUTH_STD_GRANT_TYPE_RESOURCE_OWNER_PW_CRED ); grantTypes.add( Validator.OAUTH_GRANT_TYPE_JWT_BEARER ); grantTypes.add( Validator.OAUTH_GRANT_TYPE_SAML2_BEARER ); AppWritableProfileImpl appWritableProfileImpl = new AppWritableProfileImpl(); AppWritableProfile.AppWritableSecret appWritableSecret = appWritableProfileImpl.new AppWritableSecretImpl(); appWritableSecret.setSecret("welcome1".toCharArray()); ClientScopeWritableProfile clientScopeWritableProfile = new ClientScopeWritableProfileImpl(); clientScopeWritableProfile.setAnyScopeAllowed(false); clientScopeWritableProfile.setUserConsentRequired(false); clientScopeWritableProfile.setAllowedResourceServers(Collections.<String>emptySet()); clientScopeWritableProfile.addAllowedScope("DocResourceServiceProfile.ALL"); clientScopeWritableProfile.addAllowedScope("MessagingResourceServiceProfile.ALL"); clientWritableProfile.setAllowedGrantTypes(grantTypes); clientWritableProfile.setAppId( "f35eed9e0cb3471bbd5a6a19919c7a78"); clientWritableProfile.setAppSecret(appWritableSecret); clientWritableProfile.setClientScopeProfile(clientScopeWritableProfile); clientWritableProfile.setClientType(ClientProfile.ClientType.CONFIDENTIAL_CLIENT); clientWritableProfile.setSequenceNumber( (new Long(sequenceNumberGenerator.get()).longValue() )); CProfiles.add( clientWritableProfile ); clientWritableProfile = new ClientWritableProfileImpl(); clientScopeWritableProfile = new ClientScopeWritableProfileImpl(); clientScopeWritableProfile.setAnyScopeAllowed(false); clientScopeWritableProfile.setUserConsentRequired(false); clientScopeWritableProfile.setAllowedResourceServers(Collections.<String>emptySet()); clientScopeWritableProfile.addAllowedScope("DocResourceServer.ALL"); clientWritableProfile.setAllowedGrantTypes(grantTypes); clientWritableProfile.setAppId( "a0709401479645c2923142e04dbd483f"); clientWritableProfile.setAppSecret(appWritableSecret); clientWritableProfile.setClientScopeProfile(clientScopeWritableProfile); clientWritableProfile.setClientType(ClientProfile.ClientType.CONFIDENTIAL_CLIENT); clientWritableProfile.setSequenceNumber( (new Long(sequenceNumberGenerator.get()).longValue() )); CProfiles.add( clientWritableProfile ); return CProfiles; } private boolean compareChars(char source[], char destion[]) { return new String(source).equals(new String(destion)); } private boolean allowToUseScopes(ClientRequest clientRequest) throws ClientSecurityManagerException { final String sourceMethod = "allowToUseScopes"; boolean result = false; ClientProfile clientProfile = getClientProfile( clientRequest.getAppId() ); if (clientProfile == null) return result; ClientScopeProfile clientScopeProfile = clientProfile.getClientScopeProfile(); if (clientScopeProfile != null && !clientRequest.getScopes().isEmpty()) { if (clientScopeProfile.isAnyScopeAllowed()) { result = true; } else if (clientScopeProfile.getAllowedScopes().containsAll(clientRequest.getScopes())) { result = true; } else { //TODO: verify resource server level scopes } } else { //some case scope is not in a part of request //for example create UT and CT creation (Identity domain) return true; } return result; } private boolean allowToUseGrantTypes(ClientRequest clientRequest) throws ClientSecurityManagerException { final String sourceMethod = "allowToUseGrantTypes"; boolean result = false; ClientProfile clientProfile = getClientProfile( clientRequest.getAppId() ); if( clientProfile == null ) return result; Collection<String> allowedGrantTypes = clientProfile.getAllowedGrantTypes(); if (allowedGrantTypes != null) { if (!clientRequest.getGrantTypes().isEmpty() && allowedGrantTypes.containsAll(clientRequest.getGrantTypes())) { result = true; } } return result; } }
リソース・サーバー・プロファイル管理プラグインは、次の機能を外部モジュールに委任します。
リソース・サーバー・プロファイル管理
このプラグインは、リソース・サーバー・リポジトリからのOAuthリソース・サーバー・プロファイルの読込みおよび書込みを行います。このプラグインを使用して、リソース・サーバー・リポジトリからOAuthリソース・サーバー・プロファイルの読込みおよび書込みを行います。このプラグインは、2つのJavaインタフェースで構成されています。一方はリソース・プロファイル読込みインタフェースで、もう一方はリソース・プロファイル書込みインタフェースです。このプラグインは、OAuthランタイム・サーバーによって実行されます。
デフォルトのプラグインはリソース・サーバー・プロファイル読込みインタフェースを実装しています。このプラグインは、OAuth MBeanリポジトリからプロファイルを読み込みます。OAuth管理者は、OAMコンソールまたはWLSTコマンド行のどちらかを使用して、OAuthリソース・サーバー・プロファイルの読込みまたは書込みを行うことができます。(コンソールおよびコマンド行は、どちらもMBean APIを使用してOAuth MBeanリポジトリと連携しています。)
リソース・サーバーは、認可リクエスト内でスコープの使用状況を通して識別されます。クライアント・アプリケーションは、認可リクエストの一部としてscope
パラメータを送信します。
スコープ・パラメータの値は、リポジトリに格納されているリソースおよびスコープ値と比較されます。定義は、oauth.xml
に格納されています。
スコープ・パラメータの値がinvalidだった場合は、クライアント・アプリケーションにinvalid_scope
エラー・レスポンスが送信されます。
スコープ・パラメータの値が有効である場合、その値は認可コードおよびアクセス・トークンとともにレスポンスに埋め込まれます。たとえば、発行されたJWT認可コードおよびアクセス・トークンには次の要求が含まれます。
"oracle.oauth.scope":"resumes.position.pmts"
アクセス・トークンを持ったクライアント・アプリケーションがリソースにアクセスすると、リソース・サーバーはアクセスを許可する前に指定されたスコープのアクセス・トークンが有効かどうかをローカルで検証するか、OAuthサービスで確認します。リソース・サーバーは、リソース・サーバーIDおよびリソース・サーバー・シークレットを認可ヘッダーのBase64値として送信します。また、このパラメータは、次のように検証済のアクセス・トークン呼出しのPOST本体としても送信されます。
grant_type
。次に例を示します。
grant_type=oracle-idm:/oauth/grant-type/resource-access-token/jwt
oracle_token_action
。次に例を示します。
oracle_token_action=validate
scope
。次に例を示します。
scope=resumes.position.pmts
assertion
。次に例を示します。
assertion=accessToken-value
最後に、OAuthサービスはアクセス・トークンを検証し、正常または無効なスコープの使用であることを示すJSONレスポンスを送信します。
カスタム・プラグインをデプロイする際には、次の注意点を参照してください。
プラグインをデプロイするには、JARファイルを次の場所にコピーします。
$ORACLE_HOME/user_projects/domains/base_domain/config/fmwconfig/oic/plugins/
カスタム・プラグインの開発にサード・パーティ製のライブラリが使用された場合、コンテナ(WebLogicまたはWebSphere)のクラスパスでライブラリが使用可能である必要があります。
JARファイルのデプロイ後に、管理対象サーバーを再起動します。
OAMコンソールを使用して新しいクライアント・セキュリティ・プラグインを作成します。
OAMコンソールにログインします。
「起動パッド」から、「OAuthサービス」→対象のドメイン→「OAuthプラグイン」→「リソース・サーバー・プロファイル・プラグイン」を選択します。
新しいプラグインを作成します。
詳細は、スクリーン・キャプチャを参照してください。
「起動パッド」から、「OAuthサービス」→対象のドメイン→「OAuthサービス・プロファイル」→OAuthサービス・プロファイルを選択します。
「プラグイン」セクションでメニューからリソース・サーバー・プロファイル・プラグインを選択し、サービス・プロファイルに割り当てます。
「クライアントにすべてのリソース・サーバーへのアクセスを許可する」オプション(「カスタム・リソース・サーバー」の下にあります)が有効になっていることを確認します。
注意: パスワード・フィールドは、デモ用に表示されています。かわりに別のリテラルを使用してください。パスワードまたはシークレット・リテラルを使用すると、システムはその値をサード・パーティ・プラグインのコードからは取得することができないデプロイメントの資格証明記憶域に格納します。 |
カスタム・プラグインを開発する際には、次の注意点を参照してください。
プラグインの開発およびコンパイルには、次のJARファイルが必要です。
oauth_common.jar
oic_common.jar
ms_oauth.jar
スコープに関連するコレクションの使用方法に注意してください。フレームワークはListおよびHashSetタイプを使用しているため、実装でキャストを行ってはいけません。キャストを行うと、ClassCast例外が発生します。
スコープの説明を取得するためには、ロケールがプラグインの実装に設定されている必要があります。そうでない場合は、NULLポインタ例外が発生します。次のようにif
条件を書き換えて、この動作を変更できます。
@Override public ScopeDescriptionProfile getScopeDescription(Locale locale) { if (this.scopeDescriptionProfiles != null) { for (ScopeDescriptionProfile scopeDescriptionProfile : scopeDescriptionProfiles) { if (scopeDescriptionProfile.getLocale().equals(locale)) { return scopeDescriptionProfile; }
package com.db; import oracle.security.idaas.oauth.common.provider.exception.OAuthMisconfigurationException; import oracle.security.idaas.oauth.resourceserver.*; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** * Custom resource plug-in sample. This plug-in demonstrates the development of a custom plug-in * for resource server profile service. In this specific sample, resource server profiles are * defined in a database. So the logic typically revolves around CRUD operations, and in this * specific sample various retrieval methods are implemented, which get used by the OAuth server * runtime. */ public class ACMEResourceSvrPlugin implements ResourceServerProfileService { //plugin config Map<String, Object> pluginConfig = new HashMap<String, Object>(); // database util gets used for CRUD operations DBUtil dbUtil = new DBUtil(); @Override public void init(Map<String, Object> pluginAttrs) throws OAuthMisconfigurationException { //initialize plugin config as set pluginConfig = pluginAttrs; // } @Override public void destroy() { // destroy plugin config pluginConfig.clear(); } @Override public ResourceServerProfile readResourceServerProfile(ResourceServerProfileRequest resourceServerProfileRequest) throws ResourceServerProfileNotFoundException { // get resource server profile based on resource server name return dbUtil.getResSvrByName(resourceServerProfileRequest.getAppName(), pluginConfig); } @Override public Collection<ResourceServerProfile> readResourceServerProfiles(ResourceServerProfileRequest resourceServerProfileRequest) throws ResourceServerProfileNotFoundException { // get all resource server profiles return dbUtil.getAllResSvrs(pluginConfig); } @Override public Collection<ResourceServerProfile> readResourceServerProfileByRequestedScope(ResourceServerProfileRequest resourceServerProfileRequest) throws ResourceServerProfileServiceException { // get all resource server profiles based on requested scopes return dbUtil.searchWithScopesList(resourceServerProfileRequest.getRequestedScopes(), pluginConfig); } @Override public ResourceServerProfile write(ResourceServerWritableProfile resourceServerWritableProfile) throws ResourceServerProfileNameAlreadyUsedException, ResourceServerProfileIdAlreadyUsedException, ResourceServerProfileServiceException { //not implemented throw new UnsupportedOperationException(); } @Override public ResourceServerProfile update(ResourceServerWritableProfile resourceServerWritableProfile) throws ResourceServerProfileNotFoundException, ResourceServerProfileServiceException { // not implemented throw new UnsupportedOperationException(); } @Override public void delete(ResourceServerProfileRequest resourceServerProfileRequest) throws ResourceServerProfileNotFoundException, ResourceServerProfileServiceException { // not implemented throw new UnsupportedOperationException(); } }
DBUtil.javaのコード例
package com.db; import oracle.security.idaas.oauth.common.appinfra.AppWritableProfile; import oracle.security.idaas.oauth.common.appinfra.impl.AppWritableProfileImpl; import oracle.security.idaas.oauth.resourceserver.*; import oracle.security.idaas.oauth.resourceserver.impl.*; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.util.*; /** * Custom resource plug-in sample. This is a utility class which interacts with the database * for doing CRUD operations. This specific sample shows how various retrieval methods are * implemented. */ public class DBUtil { private final static String CONN_URL = "connUrl"; private final static String CONN_USER = "user"; private final static String CONN_USER_PWD = "password"; /** * Get resource profile by resource server profile name * @param resName * @param pluginConfig * @return * @throws ResourceServerProfileNotFoundException */ public ResourceServerProfile getResSvrByName(String resName, Map<String, Object> pluginConfig) throws ResourceServerProfileNotFoundException { Connection dbcon; ResourceServerWritableProfile resProfile = null; try { dbcon = DriverManager.getConnection((String) pluginConfig.get(CONN_URL), (String) pluginConfig.get(CONN_USER), (String) pluginConfig.get(CONN_USER_PWD)); Statement stmt = dbcon.createStatement(); System.out.println("searchWithScopesList: BEFORE LIST"); String resQuery = "select NAME ," + " AUTHZ_PLUGIN_REF ," + " SCOPE_PREFIX ," + " REFRESH_TOKEN_EXPIRY_TIME ," + " ACCESS_TOKEN_OVERRIDDEN ," + " DOMAIN_UUID ," + " AUD_CLAIM ," + " ALLOW_TOKEN_ATTR_RETRIEVAL ," + " LAST_UPDATE_TIME ," + " ID ," + " DESCR ," + " ACCESS_TOKEN_EXPIRY_TIME ," + " OFFLINE_SCOPE_NAME," + " OVERRIDE_TOKEN_SETTINGS" + " from oauth_resource_svr where name = '" + resName + "'" ; ResultSet rslt = stmt.executeQuery(resQuery); if (rslt.next()) { resProfile = new ResourceServerWritableProfileImpl(); System.out.println("searchWithScopesList: RES: " + rslt.getString("id") + "\t" + rslt.getString("name")); String resId = rslt.getString("id"); populateResSvrPrimaryData(rslt, resProfile); //get scopes data and populate resource profile populateScopesData(resId, stmt, resProfile); //get static and dynamic attributes data and populate them populateTokenCustomAttributes(resId, stmt, resProfile); //get resource attributes data populateResSvrAttributes(resId, stmt, resProfile); } dbcon.close(); } catch (Exception ex) { System.out.println(ex); } if (resProfile == null) { throw new ResourceServerProfileNotFoundException("Invalid resource name: " + resName ); } return resProfile; } /** * Get all resource server profiles. * @param pluginConfig * @return * @throws ResourceServerProfileNotFoundException */ public Collection<ResourceServerProfile> getAllResSvrs(Map<String, Object> pluginConfig) throws ResourceServerProfileNotFoundException { Connection dbcon; List<ResourceServerProfile> listResources = new ArrayList<ResourceServerProfile>(); try { dbcon = DriverManager.getConnection((String) pluginConfig.get(CONN_URL), (String) pluginConfig.get(CONN_USER), (String) pluginConfig.get(CONN_USER_PWD)); Statement stmt = dbcon.createStatement(); System.out.println("searchWithScopesList: BEFORE LIST"); String resQuery = "select NAME ," + " AUTHZ_PLUGIN_REF ," + " SCOPE_PREFIX ," + " REFRESH_TOKEN_EXPIRY_TIME ," + " ACCESS_TOKEN_OVERRIDDEN ," + " DOMAIN_UUID ," + " AUD_CLAIM ," + " ALLOW_TOKEN_ATTR_RETRIEVAL ," + " LAST_UPDATE_TIME ," + " ID ," + " DESCR ," + " ACCESS_TOKEN_EXPIRY_TIME ," + " OFFLINE_SCOPE_NAME," + " OVERRIDE_TOKEN_SETTINGS" + " from oauth_resource_svr " ; ResultSet rslt = stmt.executeQuery(resQuery); while (rslt.next()) { ResourceServerWritableProfile testProfile = new ResourceServerWritableProfileImpl(); System.out.println("searchWithScopesList: RES: " + rslt.getString("id") + "\t" + rslt.getString("name")); String resId = rslt.getString("id"); populateResSvrPrimaryData(rslt, testProfile); //get scopes data and populate resource profile populateScopesData(resId, stmt, testProfile); //get static and dynamic attributes data and populate them populateTokenCustomAttributes(resId, stmt, testProfile); //get resource attributes data populateResSvrAttributes(resId, stmt, testProfile); listResources.add(testProfile); } dbcon.close(); } catch (Exception ex) { System.out.println(ex); } if (listResources.isEmpty()) { throw new ResourceServerProfileNotFoundException("Not able to retrieve any resources, may be resource table has no data"); } return listResources; } /** * Get resource server profiles based on requested scopes. * @param scopeList * @param pluginConfig * @return * @throws ResourceServerProfileNotFoundException */ public Collection<ResourceServerProfile> searchWithScopesList(Collection<String> scopeList, Map<String, Object> pluginConfig) throws ResourceServerProfileNotFoundException { Connection dbcon; List<ResourceServerProfile> listResources = new ArrayList<ResourceServerProfile>(); try { dbcon = DriverManager.getConnection((String) pluginConfig.get(CONN_URL), (String) pluginConfig.get(CONN_USER), (String) pluginConfig.get(CONN_USER_PWD)); Statement stmt = dbcon.createStatement(); System.out.println("searchWithScopesList: BEFORE LIST"); String scopeQueryPat = "select id from oauth_resource_svr_scope where name in (%s)"; String scopeQuery = String.format(scopeQueryPat, prepareInQueryPart(scopeList)); System.out.println("searchWithScopesList: scopeQuery :" + scopeQuery); String resQuery = "select NAME ," + " AUTHZ_PLUGIN_REF ," + " SCOPE_PREFIX ," + " REFRESH_TOKEN_EXPIRY_TIME ," + " ACCESS_TOKEN_OVERRIDDEN ," + " DOMAIN_UUID ," + " AUD_CLAIM ," + " ALLOW_TOKEN_ATTR_RETRIEVAL ," + " LAST_UPDATE_TIME ," + " ID ," + " DESCR ," + " ACCESS_TOKEN_EXPIRY_TIME ," + " OFFLINE_SCOPE_NAME," + " OVERRIDE_TOKEN_SETTINGS" + " from oauth_resource_svr where id in (" + scopeQuery + ")" ; ResultSet rslt = stmt.executeQuery(resQuery); while (rslt.next()) { ResourceServerWritableProfile testProfile = new ResourceServerWritableProfileImpl(); System.out.println("searchWithScopesList: RES: " + rslt.getString("id") + "\t" + rslt.getString("name")); String resId = rslt.getString("id"); populateResSvrPrimaryData(rslt, testProfile); //get scopes data and populate resource profile populateScopesData(resId, stmt, testProfile); //get static and dynamic attributes data and populate them populateTokenCustomAttributes(resId, stmt, testProfile); //get resource attributes data populateResSvrAttributes(resId, stmt, testProfile); listResources.add(testProfile); } dbcon.close(); } catch (Exception ex) { System.out.println(ex); } if (listResources.isEmpty()) { throw new ResourceServerProfileNotFoundException("Invalid scopes: " + scopeList ); } return listResources; } /** * Populates the resource server profile writable instance with data retrieved from the * database * @param rslt * @param resourceServerWritableProfile */ private static void populateResSvrPrimaryData(ResultSet rslt, ResourceServerWritableProfile resourceServerWritableProfile) { try { resourceServerWritableProfile.setAppId(rslt.getString("ID")); resourceServerWritableProfile.setAppName(rslt.getString("NAME")); resourceServerWritableProfile.setIdentityDomainUUID(rslt.getString("DOMAIN_UUID")); resourceServerWritableProfile.setAudienceClaimValue(rslt.getString("AUD_CLAIM")); resourceServerWritableProfile.setAuthzUserConsentPluginRef(rslt.getString("AUTHZ_PLUGIN_REF")); resourceServerWritableProfile.setOfflineScopeName(rslt.getString("OFFLINE_SCOPE_NAME")); resourceServerWritableProfile.setScopeNamespacePrefix(rslt.getString("SCOPE_PREFIX")); if ("Y".equalsIgnoreCase(rslt.getString("OVERRIDE_TOKEN_SETTINGS"))) { Long rtExp = rslt.getLong("REFRESH_TOKEN_EXPIRY_TIME"); if (rtExp != null ) { System.out.println("RES: REFRESH_TOKEN_EXPIRY_TIME " + rslt.getLong("REFRESH_TOKEN_EXPIRY_TIME")); OAuthRefreshableTokenWritableProfile oAuthRefreshableTokenWritableProfile = new OAuthRefreshableTokenWritableProfileImpl(); oAuthRefreshableTokenWritableProfile.setRefreshTokenExpiresIn(rtExp); resourceServerWritableProfile.setOverriddenAccessTokenProfile(oAuthRefreshableTokenWritableProfile); } Long atExp = rslt.getLong("ACCESS_TOKEN_EXPIRY_TIME"); if ( atExp != null ) { OAuthTokenWritableProfile oAuthTokenWritableProfile = new OAuthTokenWritableProfileImpl(); oAuthTokenWritableProfile.setExpiresIn(atExp); resourceServerWritableProfile.setOverriddenAuthzCodeTokenProfile(oAuthTokenWritableProfile); } } AppWritableProfile.AllowedTokenAttributesRetrievalWritableProfile allowedTokenAttributesRetrievalWritableProfile = new AppWritableProfileImpl().new AllowedTokenAttributesRetrievalWritableProfileImpl(); if ("Y".equalsIgnoreCase(rslt.getString("ALLOW_TOKEN_ATTR_RETRIEVAL"))) { allowedTokenAttributesRetrievalWritableProfile.setAllTokenAttributesRetrievalAllowed(true); } else { allowedTokenAttributesRetrievalWritableProfile.setAllTokenAttributesRetrievalAllowed(false); } resourceServerWritableProfile.setAllowedTokenAttributesRetrieval(allowedTokenAttributesRetrievalWritableProfile); } catch (Exception e) { System.out.println("ACMEResourceSvrPlugin: DBUtil: populateResSvrPrimaryData: " + e) ; } } /** * Populates resource server profile writable instance with scopes data. * @param resId * @param stmt * @param resourceServerWritableProfile */ private static void populateScopesData(String resId, Statement stmt, ResourceServerWritableProfile resourceServerWritableProfile) { try { String scopeQuery1 = "select name, " + "descr, " + "user_consent_required " + "from OAUTH_RESOURCE_SVR_SCOPE where id='" + resId + "'"; ResultSet scopeRslt = stmt.executeQuery(scopeQuery1); while (scopeRslt.next()) { System.out.println("RES SCOPES: " +scopeRslt.getString("name") + "\t" + scopeRslt.getString("descr")); ScopeWritableProfile scopeProfile = new ScopeWritableProfileImpl(); //set scope value scopeProfile.setName(scopeRslt.getString("name")); //set user consent required flag if ("Y".equalsIgnoreCase(scopeRslt.getString("user_consent_required"))) { scopeProfile.setUserConsentRequired(true); } else { scopeProfile.setUserConsentRequired(false); } // set scope description if (scopeRslt.getString("descr") != null) { ScopeWritableProfile.ScopeDescriptionWritableProfile scopeDescriptionWritableProfile = new ScopeWritableProfileImpl.ScopeDescriptionWritableProfileImpl(); scopeDescriptionWritableProfile.setDescription(scopeRslt.getString("descr")); scopeDescriptionWritableProfile.setLocale(Locale.ENGLISH); scopeProfile.addScopeDescription(scopeDescriptionWritableProfile); } resourceServerWritableProfile.addScopeProfile(scopeProfile); } } catch (Exception e) { System.out.println("ACMEResourceSvrPlugin: DBUtil: populateScopesData: " + e) ; } } /** * Populates resource server profile writable instance with access token's custom attributes * data. * @param resId * @param stmt * @param resourceServerWritableProfile */ private static void populateTokenCustomAttributes(String resId, Statement stmt, ResourceServerWritableProfile resourceServerWritableProfile) { try { TokenAttributeWritableProfile tokenAttributeWritableProfile = new TokenAttributeWritableProfileImpl(); String stQuery = "select name, " + "value " + "from OAUTH_RESOURCE_SVR_AT_STATTRS where id='" + resId + "'"; ResultSet stRslt = stmt.executeQuery(stQuery); while (stRslt.next()) { System.out.println("RES STATIC ATTRS: " + stRslt.getString("name") + "\t" + stRslt.getString("value")); tokenAttributeWritableProfile.addTokenStaticAttribute(stRslt.getString("name"), stRslt.getString("value")); } //get dynamic attributes data String dyQuery = "select name " + "from OAUTH_RESOURCE_SVR_AT_DYNATTRS where id='" + resId + "'"; ResultSet dyRslt = stmt.executeQuery(dyQuery); while (dyRslt.next()) { System.out.println("RES DYN ATTRS: " +dyRslt.getString("name")); tokenAttributeWritableProfile.addTokenDynamicAttribute(dyRslt.getString("name")); } resourceServerWritableProfile.setTokenAttributeProfile(tokenAttributeWritableProfile); } catch (Exception e) { System.out.println("ACMEResourceSvrPlugin: DBUtil: populateTokenCustomAttributes: " + e) ; } } /** * Populates resource server profile writable instance with additional attributes data. * @param resId * @param stmt * @param resourceServerWritableProfile */ private static void populateResSvrAttributes(String resId, Statement stmt, ResourceServerWritableProfile resourceServerWritableProfile) { try { String attrsQuery = "select name, " + "value " + "from OAUTH_RESOURCE_SVR_ATTRS where id='" + resId + "'"; ResultSet attrsRslt = stmt.executeQuery(attrsQuery); Map<String, String> attrs = new HashMap<String, String>(); while (attrsRslt.next()) { System.out.println("RES ATTRS: " +attrsRslt.getString("name") + "\t" + attrsRslt.getString("value")); attrs.put(attrsRslt.getString("name"), attrsRslt.getString("value")); } if (!attrs.isEmpty()) { resourceServerWritableProfile.setAttributes(attrs); } } catch (Exception e) { System.out.println("ACMEResourceSvrPlugin: DBUtil: populateResSvromAttributes: " + e) ; } } private static String prepareInQueryPart(Collection<String> listStr) { StringBuilder builder = new StringBuilder(); Iterator itr = listStr.iterator(); for (int i = 0; i < listStr.size();) { builder.append("'"); //builder.append(listStr.get(i)); builder.append((String)itr.next()) ; builder.append("'") ; if (++i < listStr.size()) { builder.append(","); } } System.out.println("ACMEResourceSvrPlugin: prepareInQueryPart: " + builder.toString()) ; return builder.toString(); } }
OAuthサーバーは、サーバー構成に基づいてカスタム属性をアクセス・トークンに追加する方法を提供しています。属性には、次の2つのタイプがあります。
静的属性 - 属性名と値のペアとして定義されます。値は、属性の定義時に固定されます。たとえば、name1=value1
です。
動的(ユーザー)属性 - この属性は、ユーザー・プロファイル固有の属性に限られています。OAuthサーバーは、ユーザー・プロファイル属性のソースを指定する必要があります。ユーザー・プロファイル・サービスは、属性名および属性値の取得に使用される可能性があります。
静的および動的属性は、IDM OAuthトークン・サーバー内の2つのエンティティである、(1) OAuthサービス・プロファイル、(2)リソース・サーバーのために定義できます。
静的属性名は、動的属性名と競合してはいけません。動的属性名はシステムによって認識されているため、名前の競合は回避されます。ただし、静的属性をまず定義して同じ名前の関連する動的属性をシステムに後から導入するレア・ケースでは、最初に静的属性を削除する必要があります。
同じ属性が両方のエンティティで定義された場合は、リソース・サーバー・ベースの属性がアクセス・トークンに移入されることが検討されます。
動的属性はユーザーに関連付けられているため、(構成されている場合は)ユーザーに同意を要求するユーザー同意ページが表示されます。ユーザーは、構成された属性が情報目的でクライアントやリソースと共有されることを承認します。
カスタム属性は、アクセス・トークン内で要求としてそのまま表示されます。OAuthサーバーは、標準のJWT要求とともにOAuthサーバー固有の要求を含むJWTベースのアクセス・トークンを発行します。次に例を示します。
標準
"exp":1357596398000, "iat":1357589198000, "aud":"oam_server1", "iss":"OAuthServiceProfile", "prn":null, "jti":"340c8324-e49f-43cb-ba95-837eb419e068",
OAuthサーバー固有
注意: 次の例は、2-legged、3-leggedおよびWeb、モバイルのシナリオによって、わずかに異なる可能性があります。 |
"oracle.oauth.user_origin_id":"john101", "oracle.oauth.user_origin_id_type":"LDAP_UID", "oracle:idm:claims:client:macaddress":"1C:AB:A7:A5:F0:DC", "oracle.oauth.scope":"brokerage", "oracle.oauth.client_origin_id":"oauthssoapp1id", "oracle.oauth.grant_type":"oracle-idm:/oauth/grant-type/resource-access-token/jwt"
前述の要求は、OAuthサーバーが生成するアクセス・トークンの一部として本質的に使用可能です。カスタム属性はJWTベースのアクセス・トークンで要求として表示されるため、次の名前付け制限に従う必要があります。
JWT標準要求名は使用しないでください。
Oracle接頭辞は使用しないでください(前述のとおり)。
予約語は使用しないでください。
属性を定義する際には、次の点に注意します。
OAuthサービス・プロファイルには、0個からn個の静的および動的属性がある可能性があります。
リソース・サーバーには、0個からn個の静的および動的属性がある可能性があります。
各スコープには、0個からn個の静的および動的属性がある可能性があります。
静的および動的のタイプ・インジケータが実行時に必要となる可能性があります。
カスタム・プラグインをデプロイする際には、次の注意点を参照してください。
プラグインをデプロイするには、JARファイルを次の場所にコピーします。
$ORACLE_HOME/user_projects/domains/base_domain/config/fmwconfig/oic/plugins/
カスタム・プラグインの開発にサード・パーティ製のライブラリが使用された場合、コンテナ(WebLogicまたはWebSphere)のクラスパスでライブラリが使用可能である必要があります。
JARファイルのデプロイ後に、管理対象サーバーを再起動します。
OAMコンソールを使用して新しいクライアント・セキュリティ・プラグインを作成します。
OAMコンソールにログインします。
「起動パッド」から、「OAuthサービス」→対象のドメイン→「OAuthプラグイン」→「カスタム・トークン属性プラグイン」を選択します。
新しいプラグインを作成します。詳細は、次のスクリーン・キャプチャを参照してください。
「起動パッド」から、「OAuthサービス」→対象のドメイン→「OAuthサービス・プロファイル」→OAuthサービス・プロファイルを選択します。
「プラグイン」セクションでメニューからカスタム・トークン属性プラグインを選択し、サービス・プロファイルに割り当てます。
package mypackage; import java.util.Map; import oracle.security.idaas.rest.provider.plugin.HandlerRequest; import oracle.security.idaas.rest.provider.plugin.HandlerResult; import oracle.security.idaas.rest.provider.plugin.SecurityHandler; public class CustomTokenAttrsPlugin implements SecurityHandler { public CustomTokenAttrsPlugin() { super(); System.out.println("CustomTokenAttrsPlugin: in the constructor"); } public void init(Map<String, String> config) { System.out.println("CustomTokenAttrsPlugin: in the constructor init: " + config.toString()); } // get token claims from the config and add them to handler result public void processSecurityEvent(HandlerRequest req, HandlerResult result) { String svcDynAttr1 = "thirdparty-svc-username"; String svcStaticAttr1 = "thirdparty-svcs-st1"; String rsrcDynAttr1 = "thridparty-rs-empnumber"; String rsrcStaticAttr1 = "thridparty-rs-st1"; result.addTokenClaim(svcStaticAttr1, "my-svc-st1-val"); result.addTokenClaim(svcDynAttr1, "my-svc-margchou"); result.addTokenClaim(rsrcStaticAttr1, "my-rs-st1-val"); result.addTokenClaim(rsrcDynAttr1, "my-rs-123456"); result.setResultStatus(HandlerResult.ResultStatus.ALLOW); } public void destroy() { final String METHOD = "destroy()"; } }
認可および承認サービス・プラグインは、ユーザー承認ベースの認可の間の一般的な認可を取り扱います。このプラグインは、生成されたトークンの要求にも影響を及ぼす可能性があります。このプラグインには、リソース認可サービスおよびユーザー承認サービスの2つの部分があります。
リソース認可サービス
IDM OAuthサービス・フレームワークは、OAuthアクセス・トークン・リクエスト、認可コード・リクエストおよびOAuthアクセス・トークン検証リクエストを処理する前にリソース認可セキュリティ・ハンドラ・プラグインを起動します。その後、サービスは認可の決定(allowedまたはdenied)を返します。
リソース認可サーバー・プラグインの起動時に、OAuthサービスは次の情報をプラグインに送信します。
リクエストされたスコープ、権限付与タイプ、クライアントIPアドレス、ユーザーおよびクライアント認証ステータス、クライアント・プロファイルおよびリソース・サーバー・プロファイル
リソース・サーバー認可プラグインは、リクエストされたデータを使用して認可の決定を行います。認可の決定が許可だった場合、リソース認可サービス・プラグインはアクセス・トークンに権限ペイロード要求を追加することもできます。
ユーザー承認サービス
ユーザー承認サービスは、ユーザーID、クライアントID、スコープ名、OAuthサービス・プロファイルおよびアイデンティティ・ドメインに基づくユーザー承認の格納、取得および取消しを行います。OAuthサーバー・フレームワークは、承認を必要とするスコープに対する認可コードおよびアクセス・トークン・リクエストを処理する際に、ユーザー承認サービスを起動します。
注意: OAuth管理者は、OAuthサービス・プロファイルの構成時にリソース認可およびユーザー承認プラグインを定義できます。管理者は、リソース・サーバー・プロファイルを構成することによってこのプラグインの参照をオーバーライドすることも可能です。 |
このプラグインは、リクエストされたスコープに対するアクセス・トークンの作成および認可コードの作成時に起動されます。このプラグインを、アイデンティティ・クライアントの作成およびユーザー・トークンの作成(JWTユーザー/クライアント・アサーション)のために起動することはできません。
認可および承認プラグインには、OAuthデータベース承認リポジトリによるデフォルト認可、RESTコールバック認可プラグイン(RAS用)の2種類の異なる実装があります。
デフォルト認可およびユーザー承認プラグインの実装
デフォルトのプラグインは、認可およびユーザー承認の両方のサービスを実装しています。ユーザー承認は、OAuthデータベース・リポジトリを使用して管理されています。アクセス・トークンの作成および検証リクエストを処理する際に、ユーザーがすでにリクエストされたスコープに対する承認を得ているかどうかが認可サービスの実装によって確認されます。
RESTコールバック・リソース認可プラグインの実装
このRESTコールバック・プラグインは、認可サービス・インタフェースのみを実装しています。これは、外部認可サービスが認可の決定を行うためにデプロイされている場合に使用されます。このプラグインにはユーザー承認が実装されていないため、対象となるユースケースは2-legged OAuthです。起動の順番は次のようになります。
OAuthサービス・フレームワーク→RESTコールバック・リソース認可プラグイン→(リモート) RAS RESTサービス
OAuth開発者は、このプラグインを使用してカスタム・リソース認可サービス用のREST実装を記述し、それをリモート・サーバーにデプロイできます。OAuth管理者は、OAMコンソールまたはWLSTコマンドを使用してRESTコールバック認可プラグイン構成内にREST認可エンドポイントを定義できます。