Microsoft Graphを使用して、ユーザがグループに属しているかどうか調べる

うちの社内アプリの認証方法は今までバラバラで、メンテナンスとか非常に大変だったので、今後はAzure ADに統一していきたいなと思っている今日この頃。

とりあえず、Microsoft Graph APIを使用して、認証とアクセス権チェック(指定グループに属しているかどうかで判断する)を試してみた。

dotnet core 3.1コンソールアプリで実装。
まず、プロジェクトにMicrosoft.Graph,Microsoft.Graph.Auth,Microsoft.Identity.Clientパッケージを追加する。

Webアプリではないので、今回は前回と同様、ユーザ名・パスワード指定でTokenを取得する。(実際の取得はGraph API内部で行う)

IPublicClientApplication app = PublicClientApplicationBuilder
    .Create(ApplicationID)
    .WithAuthority(Authority)
    .Build();
// Scopeはユーザ情報の読取とグループ情報の読取があればいいかな
string[] scopes = new string[] { "https://graph.microsoft.com/User.Read", "https://graph.microsoft.com/Group.Read.All" };

// ユーザ名・パスワードでアクセスするので、Microsoft.Graph.Auth.UserNamePasswordProviderを使用する
var provider = new UsernamePasswordProvider(app,scopes);

// Microsoft.GraphGraphServiceClientを作成
var graphClient = new GraphServiceClient(provider);

// ログインユーザの情報を取得する
var me = await graphClient.Me.Request()
    .WithUsernamePassword(username,secPwd)
    .GetAsync();

// ログインユーザが属している、グループを取得する
var members = await graphClient.Me.MemberOf.Request().GetAsync();
if (members != null) {
    foreach(var m in members) {
        if (m.GetType() == typeof(Group)) {
            Group g = (Group)m;
            Console.WriteLine($"{g.Id} : {g.DisplayName}");
        }
    }
}

MemberOfは自分が属しているグループの一覧なので、このグループが他のグループに属している場合、そのグループに属していることはこの一覧からは確認できない。

つまり、以下のような状態で、自分が最終的にAグループに属しているかどうかはこの一覧からは分からないということ。MemberOfで取得されるのは、BグループとCグループとなるからだ。

こういうグループの入れ子は配布リストなどには良く見られるよね。セキュリティグループはこういう設計をしない方が良いとは思うけど、場合によりけりだろうか・・・

で、自分がAグループに属しているかどうかを調べるには、グループを手繰っていく必要がある。効率はともかくとして、とりあえず以下のようなメソッドを作ってみた。

// グループを手繰って、指定グループに属しているかどうか調べる
// 最初に指定するグループが指定グループ
static async Task<bool> IsMember(Group g,string mail,GraphServiceClient graphClient,string username, SecureString secPwd) {
    foreach(var m in g.Members) {
        if (m.GetType() == typeof(User)) { // メンバーがユーザならメールアドレスをチェック
            User u = (User)m;
            if (u.Mail == mail) {
                return true;
            }
        } else if (m.GetType() == typeof(Group)) { // グループならグループ内をチェック
            Group sg = (Group)m;
            var grps = await graphClient.Groups.Request()
                .WithUsernamePassword(username,secPwd)
                .Expand("Members")	// これを入れないと、Membersが取れない
                .Filter($"id eq '{sg.Id}'")	// IDを指定
                .GetAsync();
            var grp = grps.FirstOrDefault();
            if (grp != null) {
				// 再帰呼出し
                return await IsMember(grp,mail,graphClient,username,secPwd);
            } else {
                return false;
            }
        }
    }
    return false;
}

グループ深度が深くなれば、何回もhttpリクエストが発生するので、効率的ではないとは思うけど・・・
まぁ、結果はOKでした。

takezou について

ソフトウェア開発会社(ITと言う言葉は大嫌い)で働く、元技術者。 未だに、社内システム位は作ってますが・・・ プログラミング言語はC#が好き。
カテゴリー: C#, Microsoft Graph, OAuth2, 技術系 パーマリンク

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください