※この記事は3-shake Advent Calendar 2023の20日目の記事です
はじめまして、@bayobayo0324 です。
株式会社スリーシェイクでクラウド型データ連携ツール「Reckoner(レコナー)」のプロダクトエンジニアしています。
去年も書いていたAdvent Calendarを今年も書いてみんとす、と考えてたらもう前日!?と愕然としております😇
何を書こうかなー?と色々考えてみたんですが、結局去年のAdvent Calendarの記事と関連のある内容にしました。
ちなみに検討中のSlack(times)はこちら
マッチングアプリが気になるという投票が1票だけありましたね😂
ほんとは以前書いたPRについてをもうちょっと広げた自分なりのテキストコミュニケーション術とかも書こうと思ったのですがそれはまたの機会にします🙇
この記事について
弊プロダクトのReckonerで、テーブル構造を変更する改修とともに認可・権限管理をCasbinから自前で設計&実装に変更したときに感じたことをつらつらと書いてます。
感じたこと is
- テーブル構造(=リソース階層)が増えることによる既存テーブル構造とロジック変更の難しさ
- 認証に影響が出たり既存API I/Fの影響だったりでリリース時の障害リスクやマージコストが高くなりそう
→リリースを段階的に行うことで軽減できた(はず - マイルストーンを分けてリリースする方針で現マイルストーンの実装対応を重くおいていたため次マイルストーンの準備である基本設計に追いついて少し足踏みするタイミングが出てしまった
→実装する段階で初めてわかることもあるので基本設計はあくまでラフ案として決め切りすぎずに実装者が詳細設計を行うことでスピードと品質のバランスが取れることもありそうなので改善したい
- 認証に影響が出たり既存API I/Fの影響だったりでリリース時の障害リスクやマージコストが高くなりそう
- リソース階層が増えた&ライブラリ(Casbin)脱却したことによる認可設計の難しさ
- 結局捨てることになる既存ライブラリ利用部分のロジックを調べすぎて時間を取られてしまった
→詳細設計を書いてから実装したら既存処理に捕われすぎずに処理を刷新できたので基本に忠実にある程度複雑な処理は詳細設計を書いたほうが良い結果になる
→関数のI/F変更に対しては詳細なUTを書くことで柔軟さと品質を担保できた(はず
- 結局捨てることになる既存ライブラリ利用部分のロジックを調べすぎて時間を取られてしまった
修正内容
テーブル構造
変更前 | 変更後 |
---|---|
構造変更の影響
- team配下のリソースは基本的にはproject配下に変更になる
- team_idをFKとして持っているテーブルに対してデータ移行が必要となる
- teamにはprojectが一つ以上存在する必要がある
- 既存teamへのprojectデータ追加
- accountはprojectに所属している必要がある
- project-account紐づけ中間テーブルへのデータ移行
- 認証によって発行されるJWTClaimsにもproject情報を付与する必要がある
- 移行前後で異なるJWTClaimsによる認証Middlewareでの考慮
- 既存APIのI/Fにもproject概念が追加になりフロント含め修正箇所が多く発生
- project用の新規APIもそれなりの数が必要
マイグレーションによるリリース時ダウンタイムや利用中ユーザーがリリース後に未認証状態/APIエラーになってしまう可能性!!
どうやって解決した?
- リリースを段階的に分けることにより予め利用前の新しいテーブルデータを既存影響なくInsertしておけた
- 上記で可能な限りダウンタイムを避けるようにしたが稼働中のテーブルにカラム追加は避けたかったため、一部大量のマイグレーションを走らせるマイルストーンではメンテナンスページ切り替えを行って極力短時間のダウンタイムとなるようリリースした
- 認証処理についてもリリースの段階により新しいリソースがClaimsにある/ない両方をカバーするロジックをリリースできたことによりユーザー影響なく変更できた
権限管理
- team配下リソース各種への権限→project配下リソース各種への権限
- 別途team全体の管理権限が必要となる
- 管理方法がCasbinから自前実装
→(去年のAdvent CalenderでCasbinで管理してるって書いたのに、、、笑
変更による影響
- 既存の権限をそのまま移行すると大量のレコードが必要になり性能面と処理の複雑化の懸念が出る
- 既存の認可MiddlewareのI/Fでは賄えない新しい権限と認可パターンの発生
→既存の認可設定の修正&影響確認が必要 - リソース階層(プロジェクト)が増えたことにより認可処理とUsecase層のデータチェック処理の責務分解点の設定が必要
→究極的にはどちらも必要だがそれぞれでレスポンスHTTPコードは異なる必要がある(403、400、404)
(複雑化&大量データにより)性能が今よりも悪くなってしまう可能性!!
認可処理を間違えると不正なデータ更新が発生してしまう可能性!!
どうやって解決した?
- 実質使っていない権限とそれに関わる処理を除却した
- 既存Controller影響が最小限となるようI/F変更(リソースのサブリソース判別用の可変個引数の追加)を行った
- 要件に基づいた処理設計とフローチャートを書いて整理&設計レビューを実施して実装確度を上げた
- 新規作成した認可Usecaseについては分岐全網羅のUTを書いて品質を上げた
- 認可で行うチェックを1次リソース(権限で管理しているリソース)までと切り分け、1次リソースの子リソースについてはAPIのUsecase層チェックに委ねる方針とした
詳細設計Notionキャプチャ
※一部ロジックはマスク処理しています
※画像は開発中のものとなり実物と異なる可能性があります
(再掲)感じたこと is
- テーブル構造(=リソース階層)が増えることによる既存テーブル構造とロジック変更の難しさ
- 認証に影響が出たり既存API I/Fの影響だったりでリリース時の障害リスクやマージコストが高くなりそう
→リリースを段階的に行うことで軽減できた(はず - マイルストーンを分けてリリースする方針で現マイルストーンの実装対応を重くおいていたため次マイルストーンの準備である基本設計に追いついて少し足踏みするタイミングが出てしまった
→実装する段階で初めてわかることもあるので基本設計はあくまでラフ案として決め切りすぎずに実装者が詳細設計を行うことでスピードと品質のバランスが取れることもありそうなので改善したい
- 認証に影響が出たり既存API I/Fの影響だったりでリリース時の障害リスクやマージコストが高くなりそう
- リソース階層が増えた&ライブラリ(Casbin)脱却したことによる認可設計の難しさ
- 結局捨てることになる既存ライブラリ利用部分のロジックを調べすぎて時間を取られてしまった
→詳細設計を書いてから実装したら既存処理に捕われすぎずに処理を刷新できたので基本に忠実にある程度複雑な処理は詳細設計を書いたほうが良い結果になる
→関数のI/F変更に対しては詳細なUTを書くことで柔軟さと品質を担保できた(はず
- 結局捨てることになる既存ライブラリ利用部分のロジックを調べすぎて時間を取られてしまった
最後に
テーブル構造変更について、以前の現場でもやったことがあったのである程度ボリュームを想像できていたものの、プロダクト特性があったり、既存影響や権限管理まで考え出すと思ったより大規模な修正になってしまいました。
全体設計等の進め方はチームリーダーが引っ張ってくれているおかげで着実に進められているので本当に感謝です🙏
認可・権限管理で使っていたCasbinについてはQiitaでも2度ほど記事を書いたのに今年は自前で設計実装することになったのでとても感慨深かったです。
また、認可について改めて深く考えたときに、同じく認可ライブラリだったり認可サービスを提供しているOsoの「Authorization Academy」が考え方の参考になったり自分の設計に対しての補強情報としてとても役にたちました。
同じく認可で迷った人で見たことがない人がいればぜひ!
https://www.osohq.com/academy
道半ばではありますがなんとか対応の終わりが見えてきたところでこんなことしてます&誰かの参考になればと思い記事にさせていただきました。
最後までご覧いただきありがとうございました!!