SAML統合でSalesforceからAWSのサービスにアクセスする (2)

こんにちは、@stomitaです。

前回につづいて、SAML統合機能を利用してSalesforceからAWSのサービス(S3)を連携する方法について記します。

前回がイントロダクションであったのに対し、今回の内容は実際の設定の手順となります。それなりに間違えないように記述しているつもりですが、不備があったらすいません。

手順の中にSalesforceSAML設定を行う場面がありますが、その際の画面は英語表示に切り替えて設定することを推奨します。というのも、SalesforceSAMLまわりの設定については現時点でかなり誤訳/独自訳が多いので、SAMLの用語を知ってる人は頭がハテナ?になるし、SAML初心者も間違えて意味を認識してしまうおそれがあるからです。

なお、1. から3. までの設定はdeveloperforceの公式Wikiのドキュメントにあるので、こちらの手順でセットアップしている場合は飛ばしても構いません。

それではどうぞ。


設定手順

1. SalesforceのIdP設定

SalesforceSAMLのアイデンティティプロバイダ(IdP)として設定するには、まず、設定から「My Domain」を有効にする必要がある。

Setup > Administer > Domain Management > My Domainにアクセス

My Domainを初回設定する場合にはしばらく時間がかかるので注意。

f:id:shinichitomita:20131224160238j:plain

その後、Setup > Administer > Security Controls > Identity Providerにアクセスすると、SAML IdPとしてのSalesforceメタデータ(.xml) をダウンロードできる。

f:id:shinichitomita:20131224160327j:plain

2. Amazon AWSへのSalesforce IdPの登録

つぎに、ダウンロードしたSalesforce IdPのメタデータを利用して、Amazon AWSにIdPを登録する。

Amazon AWSに管理者としてログインし、ConsoleからIAMを開き、「Identity Providers」のメニューを開き、画面上の「Create SAML Provider」ボタンをクリックし、SAML IdPの登録を開始。

Identity Providerの名前を入力する(ここでは適当にSalesforceIdPとしている)

f:id:shinichitomita:20131224160440j:plain

次に、先ほどSalesforceからダウンロードしたメタデータXMLファイルをファイル選択してアップロードする。

Identity Providerの作成が完了したら、作成されたIdentity Providerをクリックし、SummaryタブからProvider ARNの値をチェックし、コピーして憶えておく。

f:id:shinichitomita:20131224160658j:plain

つづいて、SalesforceからのシングルサインオンでログインしたユーザのためのIAM Roleも作成しておく。AWSへのアクセス権限の設定についてはこのロールに対して行うことになる。
「Roles」メニューから「Create New Role」ボタンをクリックし、新規のRoleを作成する。

最初に作成するRole名を入力する(ここではSalesforceUserRoleとしている)

f:id:shinichitomita:20131224160834j:plain

次に、Role Typeの選択画面において「Role for Identity Provider Access」の中から「Grant API access to SAML providers 」を選択する

f:id:shinichitomita:20131224160908j:plain

続いて、Roleに割り与える権限の設定を行う。ここではPolicy Templateの中から「Amazon S3 Full Access」をとりあえず選んでおく。後々詳細に権限設定を行いたい場合は、このポリシー内容をいろいろカスタマイズすることになる。

f:id:shinichitomita:20131224160947j:plain

Roleの作成が完了したら、作成されたRoleをクリックし、Summaryタブの中に表示されているRole ARNをコピーして憶えておく。

f:id:shinichitomita:20131224161018j:plain

3. Salesforceへのアプリケーション登録(Connected Appsの登録)

再びSalesforceに戻り、今度はSAMLアサーションをうけとるアプリケーションを登録する。
SalesforceではConnected Appsという仕組みで外部アプリケーションとの接続を管理している。
Setup > Build> Create > Apps > Connected Apps から、新規Connected Appを作成する。

Connected App Nameにはアプリケーションの名前を(ここではS3 File Listとしている)、
Contact Emailには自分の電子メールを登録する。
ロゴ画像、アイコンは任意。

f:id:shinichitomita:20131224161646j:plain

ページ下方のWeb Apps Settingにある「Enable SAML」のチェックをオンにして、
Entity IDには "https://signin.aws.amazon.com/saml” を入力する
ACS URL(Assertion Consumer Service URL:SAMLアサーションを受け取ってシングルサインオンするアプリケーションのURL)にも Entity IDと同様に "https://signin.aws.amazon.com/saml“ をとりあえず入力するが、これは後で入れ替える。
Subject TypeとしてPersistent ID を選択し、Name ID Format は "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent” を選ぶ。
Issuer およびService Provider Certificate の欄はそのままにしておく。

f:id:shinichitomita:20131224161728j:plain

以上の入力をしたら、新規Connected Appの設定を保存する。

次に、Setup > Administer > Managed Apps > Connected Apps から、作成されたConnected Appの設定を確かめる。

要注意:Build > Create > Apps からもConnected Appの設定を確認・編集できるけれど、表示される内容が微妙に違うので、こちらからはアクセスしないように。
混乱の元になるのでなんとかして欲しい>Salesforceの中の人

作成したConnected Appsの設定詳細画面の下の方に、このConnected AppにアクセスするProfileの設定と、AWSのシングルサインオンに利用できるCustom Attributeの設定がある。Profilesには割り当てるプロファイルを選択し、Custom Attributeには以下のキーと値を設定する。

Attribute Key: https://aws.amazon.com/SAML/Attributes/RoleSessionName
Value: $User.Email

Attribute Key: https://aws.amazon.com/SAML/Attributes/Role
Value: ‘[AWSで作成したProviderのARN],[AWSで作成したRoleのARN]'

f:id:shinichitomita:20131224162347j:plain

次に、設定画面のLogin Information の中にある「IdP-Initiated Login URL」の値をコピーする。Editボタンを押してConnected Appの設定を編集し、Start URLに先ほどコピーしたIdP-Initiated Login URLの値を貼り付けて保存する。

f:id:shinichitomita:20131224162518j:plain

この時点で、画面右上のアプリケーションメニューに、先ほど登録したConnected Appである「S3 File List」が追加表示されているので、こちらからアクセスしてみる。

f:id:shinichitomita:20131224162548j:plain

以下のように無事AWS ConsoleにシングルサインオンができればOK。まずは一段落。

f:id:shinichitomita:20131224162756j:plain

4. Visualforceページの作成

上記で登録したACS URLには、AWSのURLを登録しているため、AWS Consoleへのログインとなってしまう。
今回はS3のファイルをリストするアプリをVisualforceページで作成し、そちらにSAMLアサーションを渡すことにする。

Setup > Build > Develop > Pages から新たにVisualforceページを作成する。

Visulforceページの名前はここではS3 File List(S3FileList)としておく。
以下はとりあえずPOSTされてきたSAMLアサーションを表示するだけのページ。

<apex:page showHeader="false"
    standardStylesheets="false"
    sidebar="false"
    contentType="text/html"
    applyBodyTag="false"
    applyHtmlTag="false"
    cache="true"
    docType="html-5.0">
<html>
    <head>
    </head>
    <body>
        SAML Response:<br/>
        {!$CurrentPage.parameters.SAMLResponse}
    </body>
</html>
</apex:page> 

Visualforceページを保存したら、Previewボタンを押して、作成したページを表示する。ただしSAML Responseには何も表示されない。
ここで、このVisualforceページのURLをブラウザのアドレスバーからコピーしておく。

Setup > Administer > Manage Apps > Connected Apps から「S3 File List」の設定に戻り、ACS URLの値をコピーしたVisualforceページのURLに変更する。

再びアプリケーションメニューから「S3 File List」を選ぶと、先ほど作成したVisualforceページが表示される。今度は実際にSAMLアサーションっぽい値が表示されていることを確認。Base64エンコードされているのでよくわからないが、デコードしてみるとSAMLアサーションを表すXMLが含まれているのがわかる。

f:id:shinichitomita:20131224163751j:plain

5. S3 Bucketの作成

次に、Salesforceからログインしたユーザがアクセスするための特別なBucketをAWS S3上に用意する。
AWS ConsoleからS3にアクセスし、新規にbucketを作成する。Bucket名は任意でOK。

Bucketが作成できたら、とりあえず、そのBucket内になんでもいいので2,3個ファイルをアップロードする。これはあとでファイルをリスト表示する際に何か表示できるようにするため。

なお、SalesforceからS3へのアクセスはクロスドメインになるので、CORS(Cross Origin Resource Sharing)の設定を忘れないようにしておく。CORS設定は、対象のBucketを選択してPropertiesタブをクリックし、Permissions セクション内の 「Add CORS Configuration」から編集可能。

f:id:shinichitomita:20131224164148j:plain

CORS設定では、ファイルアップロードや削除を行うため、AllowedMethodにはGET以外にもPOST/PUT/DELETEメソッドを許可しておく必要あり。以下CORS設定のサンプル。

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>DELETE</AllowedMethod>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

ちなみに、先ほど作ったIAM Roleには権限としてS3 Full Accessを与えているので、SalesforceからシングルサインオンしたユーザはS3内のどんなBucketにも読み書きできてしまう。
本来ならちゃんとこのBucketだけに権限を限定しておくべきだが、この記事ではひとまずその作業はおいておく。

6. AWS JavaScript SDKを利用してS3 APIをコールする

さあ、ここからやっと本題(長かった…)。VisualforceページのJavaScriptコードからAWS SDKを利用してS3のAPIにアクセスする。

先ほど作成したVisualforceページを編集して、以下のように変更する。

<apex:page showHeader="false"
    standardStylesheets="false"
    sidebar="false"
    contentType="text/html"
    applyBodyTag="false"
    applyHtmlTag="false"
    cache="true"
    docType="html-5.0">
<html>
    <head>
        <script src="//sdk.amazonaws.com/js/aws-sdk-2.0.0-rc4.min.js"></script>
        <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
        <script>
var bucketName = 'salesforce-bucket';
var bucket;
 
$(function() {
  var assertion = $('#samlResponse').val();
  if (!assertion) {
    throw Error("No assertion is given to this page. Please access from app menu.");
  }
  AWS.config.credentials = new AWS.SAMLCredentials({
    RoleArn: 'arn:aws:iam::119301928242:role/SalesforceUserRole',
    PrincipalArn: 'arn:aws:iam::119301928242:saml-provider/SalesforceIdP',
    SAMLAssertion: assertion
  });
  bucket = new AWS.S3({ params: { Bucket: bucketName } });
  bucket.listObjects({}, function(err, resp) {
    if (err) { throw err; }
    $('#fileList').empty();
    $.each(resp.Contents, function(i, content) {
      $('<li>').text(content.Key).appendTo($('#fileList'));
    });
  });
});
        </script>
    </head>
    <body>
        <input type="hidden" id="samlResponse" value="{!$CurrentPage.parameters.SAMLResponse}" />
        <ul id="fileList"></ul>
    </body>
</html>
</apex:page>

AWS JavaScript SDKをCDNから読み込むようにしている。ついでにjQueryもCDNから読み込んでいる。

SAMLアサーションを利用した初期化には、AWS.SAMLCredentialsを利用する。コンストラクタに渡すRoleArnには、AWSで作成したRole ARNを、およびPrincipalArnにはProvider ARNを設定する。SAMLAssertionにはPOSTパラメータで渡ってきたアサーションを埋め込む。

あとは、AWS JavaScript SDKを利用して、S3のbucketオブジェクトを先ほど作成したbucketを指定して作成し、listObjects APIをコールする。コールバック関数内でbucket内のファイル情報が取得できるので、jQueryを利用して適当にHTMLに表示する。

ここまできたら、再びアプリケーションメニューから「S3 File List」を選ぶ。

以下のようにS3にアップロードしたファイルが画面上にリストできていればOK。

f:id:shinichitomita:20131224165304j:plain

次回

おつかれさまでした。
でもまだやっとSAMLログインしてS3のAPIにアクセスできただけなので、次回はもう少しアプリケーションとして役に立つように整えていきます。