LoginSignup
3
0

More than 1 year has passed since last update.

ASP.NET Coreでセキュリティーを考慮したQuickSightの匿名埋め込みダッシュボード機能を利用する(後編)

Last updated at Posted at 2021-08-31

この記事はASP.NET Coreでセキュリティーを考慮したQuickSightの匿名埋め込みダッシュボード機能を利用するの後編にあたります。前編をご覧になっていない方はこちらから確認ください。

ASP.NET Coreプロジェクトの作成

dotnet コマンドでプロジェクトを作成し、必要なライブラリを追加していきます。

mkdir DashboardSample
cd DashboardSample
dotnet new webapp
dotnet add package AWSSDK.Extensions.NETCore.Setup
dotnet add package AWSSDK.QuickSight
dotnet add package AWSSDK.SecurityToken

アプリケーション初期化時に、SecurityTokenService(STS)とQuickSightをDIコンテナに追加します。

Startup.cs
using Amazon.QuickSight;
using Amazon.SecurityToken;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace DashboardSample
{
    public class Startup
    {
        // ... 略 ...
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDefaultAWSOptions(Configuration.GetAWSOptions());
            services.AddAWSService<IAmazonQuickSight>();
            services.AddAWSService<IAmazonSecurityTokenService>();
            services.AddControllersWithViews();
        }
        // ... 略 ...
    }
}

Viewの定義

埋込ダッシュボードを表示するためには、ダッシュボードのARNが必要になります。
ダッシュボード毎のARNを調べるのがめんどくさいので、一度ドロップダウンリストに現在のアカウントで利用できるダッシュボードの一覧を表示した後に、表示したいダッシュボードとダッシュボードを表示する権限を選択してダッシュボードを表示できるようにしておきます。

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="text-center">
    <form method="post">
        <div><select asp-for="SelectedDashboard" asp-items="Model.DashboardList" name="dashboard" id="dashboard"></select></div>
        <div><input type="submit" value="表示(User)" asp-page-handler="ViewDashboardForUser"/></div>
        <div><input type="submit" value="表示(Admin)" asp-page-handler="ViewDashboardForAdmin"/></div>
    </form>
    @if (!string.IsNullOrEmpty(Model.EmbedDashboardUrl))
    {
        <div style="width: 100%">
            <iframe style="width: 100%; height: 600px" src="@Model.EmbedDashboardUrl"></iframe>
        </div>
    }
</div>

ドロップダウンリストにダッシュボードの一覧が表示され、表示(User)ボタンと、表示(Admin)ボタンでそれぞれの権限のダッシュボードを表示します。
image.png

ダッシュボード一覧の表示

ページの要求時はSecurityTokenServiceを利用して、現在ASP.NET Coreを動かしているAWSアカウントのAwsAccountIdを取得し、QuickSightのListDashboardsAPIを使ってダッシュボードの一覧を取得しドロップダウンリストに表示するように変換しています。

Index.cshtml.cs
        public async Task OnGet()
        {
            var account = await _amazonSecurityTokenService.GetCallerIdentityAsync(new GetCallerIdentityRequest());
            await LoadDashBoardsAsync(account.Account);
        }
        public async Task LoadDashBoardsAsync(string awsAccountId)
        {
            // ページングしてないので注意
            var response = await _amazonQuickSight.ListDashboardsAsync(new ListDashboardsRequest
            {
                AwsAccountId = awsAccountId
            });
            DashboardList = new SelectList(
                response.DashboardSummaryList,
                nameof(DashboardSummary.DashboardId),
                nameof(DashboardSummary.Name));
        }

ダッシュボードの表示

表示(User)ボタンと、表示(Admin)ボタンクリック時にPost引数でわたってきたDashboardの値をもとにダッシュボードを表示していきます。

Index.cshtml.cs
        // `表示(Admin)`ボタンクリック時の処理
        public async Task OnPostViewDashboardForAdmin(string dashboard)
        {
            var account = await _amazonSecurityTokenService.GetCallerIdentityAsync(new GetCallerIdentityRequest());
            await LoadDashBoardsAsync(account.Account);

            SelectedDashboard = dashboard;
            EmbedDashboardUrl = await GenerateEmbedUrlForAnonymousUserAsync(account.Account ,dashboard, "user,admin");
        }
        // `表示(User)`ボタンクリック時の処理
        public async Task OnPostViewDashboardForUser(string dashboard)
        {
            var account = await _amazonSecurityTokenService.GetCallerIdentityAsync(new GetCallerIdentityRequest());
            await LoadDashBoardsAsync(account.Account);

            SelectedDashboard = dashboard;
            EmbedDashboardUrl = await GenerateEmbedUrlForAnonymousUserAsync(account.Account, dashboard, "user");
        }

まずは、DescribeDashboardAPIを使ってダッシュボードの詳細情報を取得します。

GenerateEmbedUrlForAnonymousUserAsync()
        public async Task<string> GenerateEmbedUrlForAnonymousUserAsync(string awsAccountId, string dashboardId, string role)
        {
            var dashboard = await _amazonQuickSight.DescribeDashboardAsync(new DescribeDashboardRequest
            {
                AwsAccountId = awsAccountId,
                DashboardId = dashboardId
            });

            //... 略 ...
        }

GenerateEmbedUrlForAnonymousUserAPIを使って先ほどのダッシュボードの詳細情報をもとに埋め込みダッシュボード用のURLを取得します。
SessionTags プロパティーに前編で設定したtag_roleを指定しているところに注目してください。

GenerateEmbedUrlForAnonymousUserAsync()
        public async Task<string> GenerateEmbedUrlForAnonymousUserAsync(string awsAccountId, string dashboardId, string role)
        {
            //... 略 ...

            var response = await _amazonQuickSight.GenerateEmbedUrlForAnonymousUserAsync(new GenerateEmbedUrlForAnonymousUserRequest
            {
                AwsAccountId = awsAccountId,
                Namespace = "default",
                AuthorizedResourceArns = new List<string>
                {
                    dashboard.Dashboard.Arn
                },
                ExperienceConfiguration = new AnonymousUserEmbeddingExperienceConfiguration
                {
                    Dashboard = new AnonymousUserDashboardEmbeddingConfiguration
                    {
                        InitialDashboardId = dashboardId,
                    }
                },
                SessionTags = new List<SessionTag>
                {
                    new() {Key = "tag_role", Value = role}
                },
                SessionLifetimeInMinutes = 60,
            });
        }

Index.cshtml.csのコード全体
Index.cshtml.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using Amazon.QuickSight;
using Amazon.QuickSight.Model;
using Amazon.SecurityToken;
using Amazon.SecurityToken.Model;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Logging;

namespace DashboardSample.Pages
{
    public class IndexModel : PageModel
    {
        public SelectList DashboardList { get; set; }
        public string SelectedDashboard { get; set; }
        public string EmbedDashboardUrl { get; set; }

        private readonly ILogger<IndexModel> _logger;
        private readonly IAmazonSecurityTokenService _amazonSecurityTokenService;
        private readonly IAmazonQuickSight _amazonQuickSight;

        public IndexModel(
            ILogger<IndexModel> logger,
            IAmazonSecurityTokenService amazonSecurityTokenService,
            IAmazonQuickSight amazonQuickSight
            )
        {
            _logger = logger;
            _amazonSecurityTokenService = amazonSecurityTokenService;
            _amazonQuickSight = amazonQuickSight;
        }

        public async Task OnGet()
        {
            var account = await _amazonSecurityTokenService.GetCallerIdentityAsync(new GetCallerIdentityRequest());
            await LoadDashBoardsAsync(account.Account);
        }

        public async Task OnPostViewDashboardForAdmin(string dashboard)
        {
            var account = await _amazonSecurityTokenService.GetCallerIdentityAsync(new GetCallerIdentityRequest());
            await LoadDashBoardsAsync(account.Account);

            SelectedDashboard = dashboard;
            EmbedDashboardUrl = await GenerateEmbedUrlForAnonymousUserAsync(account.Account ,dashboard, "user,admin");
        }
        public async Task OnPostViewDashboardForUser(string dashboard)
        {
            var account = await _amazonSecurityTokenService.GetCallerIdentityAsync(new GetCallerIdentityRequest());
            await LoadDashBoardsAsync(account.Account);

            SelectedDashboard = dashboard;
            EmbedDashboardUrl = await GenerateEmbedUrlForAnonymousUserAsync(account.Account, dashboard, "user");
        }

        public async Task LoadDashBoardsAsync(string awsAccountId)
        {
            // ページングしてないので注意
            var response = await _amazonQuickSight.ListDashboardsAsync(new ListDashboardsRequest
            {
                AwsAccountId = awsAccountId
            });
            DashboardList = new SelectList(
                response.DashboardSummaryList,
                nameof(DashboardSummary.DashboardId),
                nameof(DashboardSummary.Name));
        }

        public async Task<string> GenerateEmbedUrlForAnonymousUserAsync(string awsAccountId, string dashboardId, string role)
        {
            var dashboard = await _amazonQuickSight.DescribeDashboardAsync(new DescribeDashboardRequest
            {
                AwsAccountId = awsAccountId,
                DashboardId = dashboardId
            });

            var response = await _amazonQuickSight.GenerateEmbedUrlForAnonymousUserAsync(new GenerateEmbedUrlForAnonymousUserRequest
            {
                AwsAccountId = awsAccountId,
                Namespace = "default",
                AuthorizedResourceArns = new List<string>
                {
                    dashboard.Dashboard.Arn
                },
                ExperienceConfiguration = new AnonymousUserEmbeddingExperienceConfiguration
                {
                    Dashboard = new AnonymousUserDashboardEmbeddingConfiguration
                    {
                        InitialDashboardId = dashboardId,
                    }
                },
                SessionTags = new List<SessionTag>
                {
                    new() {Key = "tag_role", Value = role}
                },
                SessionLifetimeInMinutes = 60,
            });
            return response.EmbedUrl;
        }
    }
}

早速実行してみると、、、ドロップダウンにダッシュボードの一覧は表示されたものの、ページには接続が拒否されましたと表示されてしまいました。
image.png

埋め込みドメインの事前設定

QuickSightの埋め込みダッシュボード機能を利用する場合、どのドメインにダッシュボードを埋め込むかをQuickSightの管理画面から設定する必要があります。ただし、ここでlocalhostやHTTPS以外から始まるドメインを指定すると次のようなメッセージが表示され設定することができません。
image.png

運用環境にデプロイすればよいのですが、確認するのに毎回デプロイするとなるとちょっと大変なので、ngrokを利用して外部のリバースプロキシ経由でデバックできるようにしましょう。
ngrokのインストールや簡単な使い方については、次の記事が参考になります。

アプリをテストおよびデバッグするセットアップMicrosoft Teamsする

デバックサーバーがhttps://localhost:44379/で起動する場合は、次のようにngrokを起動します。

> ngrok http -host-header=localhost https://localhost:44379/

image.png
パブリックに公開された外部のURLであれば登録できます(ただし、無料のngrokだと定期的にURLが失効してしまうのでそのたびにQuickSightに登録する必要はあります)。
image.png

さて、改めて実行してみましょう。

表示(User)ボタンをクリックするとQuickSightのダッシュボードが表示され、ロールがuserのもののみ表示されています。
image.png
表示(Admin)ボタンをクリックするとadminとuserの両方が表示されていますね。
image.png

まとめ

  • AwsAccountIdSecurityTokenServiceGetCallerIdentityAPIから取得するのが簡単
  • 埋め込みURLはGenerateEmbedUrlForAnonymousUserAPIで取得し、RLSで設定したタグ情報を引き渡すことでデータのセキュリティーを担保する
  • 埋め込みダッシュボードを表示するにはあらかじめQuickSightにドメインの登録が必要、ローカルでデバックするときはngrokを利用すると簡単
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0