ニコニコ漫画と認可

D60bbe66cfd79bbb250bad66a706f939?s=47 fusagiko
September 29, 2020

 ニコニコ漫画と認可

Gotanda.rb#46 "管理画面/権限" https://gotanda-rb.connpass.com/event/187557/ にて発表した内容です。

D60bbe66cfd79bbb250bad66a706f939?s=128

fusagiko

September 29, 2020
Tweet

Transcript

  1. ニコニコ漫画と認可 2020/9/29 株式会社トリスタ フサギコ(髙﨑 尚人)

  2. 自己紹介 • 名前:フサギコ • Twitter :fusagiko • GitHub :takayamaki •

    本名 :髙﨑 尚人 • 主な分野:Ruby(Rails), Terraform(AWS) • 他に :TypeScript, React, Ansible • Ruby:2.1.2~ • 大学院生の時に修士論文用のシミュレータを Pure Rubyフルスクラッチで書いたのが最初 • 趣味:アイドルマスター • 全作品、それぞれ楽しんでるタイプ
  3. 自己紹介(OSS活動) • マストドン • 本家(tootsuite/mastodon)コントリビュータ • 2年弱PR送れてないけどまだcommit数37位らしい • imastodon.net構築、運用、管理人 •

    アイドルマスターのファン向け非公式マストドン • 2017年4月開設 • アカウント数4817、アクティブ数500/週程度 • 本家に独自改造が入っている • 完全に袂を分かったわけではないのでVariety(変種)と自称 • 追従は遅れ気味…
  4. 自己紹介(仕事) • ~ 2016/3 工学院大学大学院 • 修士(工学) 修論:情報指向ネットワーク • 2016/4~2017/7

    NHN テコラス株式会社 • 2017/8~2019/6 株式会社ドワンゴ • friends.nico • ドワンゴ公式マストドン (サービス終了) • N Air • ニコニコ生放送向け 配信ソフト • 2019/7~現在 株式会社トリスタ • 読書メーター • 読書愛好家コミュニティサイト • ニコニコ漫画 • UGC/PGC漫画連載プラットフォーム
  5. 自己紹介(仕事) • ~ 2016/3 工学院大学大学院 • 修士(工学) 修論:情報指向ネットワーク • 2016/4~2017/7

    NHN テコラス株式会社 • 2017/8~2019/6 株式会社ドワンゴ • friends.nico • ドワンゴ公式マストドン (サービス終了) • N Air • ニコニコ生放送向け 配信ソフト • 2019/7~現在 株式会社トリスタ • 読書メーター • 読書愛好家コミュニティサイト • ニコニコ漫画 • UGC/PGC漫画連載プラットフォーム
  6. ニコニコ漫画と認可 2020/9/29 株式会社トリスタ フサギコ(髙﨑 尚人)

  7. ニコニコ漫画 ニコニコ漫画

  8. ニコニコ漫画 UGC/PGC漫画連載 プラットフォーム

  9. ニコニコ漫画 多くの編集部様から ご連載頂いています

  10. ニコニコ漫画はRailsで… ニコニコ漫画は Railsで作られている…

  11. ニコニコ漫画はRailsで… ニコニコ漫画は Railsで作られている…

  12. ニコニコ漫画はRailsではない 現行はPHP かつ モノリシック

  13. ニコニコ漫画はPHP 静画の派生として 漫画が始まってから10年超え 2010/8/27に特設ページ扱いで開始…らしい by Wikipedia

  14. ニコニコ漫画はPHP コード構造が結構厳しい

  15. ニコニコ漫画はPHP テストが 十分でない

  16. Railsでリプレイスしよう ビジネスロジックを Rails APIサーバとして 切り出し・リプレイスしよう

  17. Railsでリプレイスしよう 現行のPHPは Backends For Frontends的 立ち位置に寄せていく

  18. Railsでリプレイスしよう そのRails APIサーバにおける 認可機構の話をします

  19. その前に と、その前に

  20. 認証と認可 認証と認可 それぞれの意味について 念のため確認

  21. 認証と認可 認証 authentication 認可 authorization “それ”が誰であるかを 確かめること to prove that

    something is real “それ”に許可を 与えること to give permission for something
  22. このように 認可と認証は 異なる行為

  23. 認証と認可 認可 しない する 認 証 し な い 何もわからないし

    何もさせない 誰であるかは 自己申告を信じ、 操作を許可する す る それが誰かわかるが、 何もさせない それが誰かを識別し、 誰であるかによって 操作を許可する
  24. 認可 と 認証が間違われるのは 全部TwitterのOAuthが悪い いわゆるログイン連携がしたいだけなら 認証(”それ”の識別)さえできれば良かったはずなのに

  25. 全部TwitterのOAuthが悪い 閑話休題

  26. ニコニコ漫画における権限とは ニコニコ漫画で 権限管理が 最も必要な場所

  27. ニコニコ漫画で権限管理が必要な場所 編集部様からの 作品原稿の納入 作品・各話のメタ情報編集 公開期間の設定 など

  28. ニコニコ漫画で権限管理が必要な場所 他編集部の 公開前の作品が見えたり、 編集できたりしてはならない

  29. 他編集部の作品が弄れてはならない これを実現するために 権限に基づく認可を行う

  30. Rubyにおける権限管理のgemの選択肢 Rubyにおける 権限管理のgemの選択肢

  31. Rubyにおける権限管理のgem •CanCanCan •Pundit •Banken

  32. CanCanCan • ユーザを中心に、何へどのアクションを認可するか DSL的に記述 • can 【アクション】, 【対象】, 【各種条件】 •

    対象にはActive Record継承したClassも指定できる • クエリインターフェイスに統合した形で使えて便利 • Active Recordに関係ない対象はシンボルで表現 • can? 【アクション】, 【対象】で判定 • => Boolean • 対象がActive Recordモデルの場合 • Article.accessible_by(current_ability) とかできる
  33. Pundit • 認可系gemとしてはおそらくもっとも有名か • モデルを中心に、誰にどのアクションを 認可するか記述 • ActiveRecordモデルに対する操作タイミングで認 可を行う •

    複雑になってきたRailsアプリケーションではモ デルとコントローラが一対一ではなくなりがち • コントローラごとに認可条件を変えたい場合詰む
  34. Banken • コントローラのアクションメソッドと認可条件 を紐づける • アクションを中心に、誰に許可するか記述 • コントローラのアクションメソッドと対応付ける ので対象リソースは自ずと導き出せるはず

  35. 認可系gemの概観 権限記述の中心 判定タイミング Pubdit モデル モデルへの操作 Banken アクション コントローラのアクション CanCanCan

    ユーザ モデルへの操作 OR 任意タイミング
  36. ニコニコ漫画は 権限判定に どのgemを使ったか

  37. ニコニコ漫画は どれも採用しなかった

  38. どの認可系gemも使わなかった • 現行PHPとDBを共有しながらの順次リプレイス • 現行が不完全なレベルベースの認可 • モデルと必要権限が一対一で対応しない • 特定カラムの更新だけ上位権限が要る場合がある •

    アクションメソッドと必要権限も対応しない • 「このカラムの更新には上位権限が要る」といった知識を BFFに流出させてはならない • バルク(一括)操作がある • 唯一可能性があったのはCanCanCanだが… • どう考えても大規模になるのでメンテしづらそう • ユーザの権限情報のDB上の表現からCanCanCanの DSLに落とし込める確信が持てなかった
  39. gemを使わない…というか使えない 前述の通り、 新ニコニコ漫画は 既にRails的でない部分がある

  40. 決めた方針 更なるRails的でない構造や 大きな変更が襲来しても 耐えられるようにしたい

  41. そのために インターフェイスを決めて コードの独立性を保ち、 変更の影響範囲を狭める 後続処理と重複したSELECTが多くなるのは妥協、許容する

  42. 認可によって知りたいのは 誰が どのような操作を どれに対して行えるか

  43. 認可の最小限のインターフェイス authorize (主体, 操作, 対象) => boolean

  44. 認可の最小限のインターフェイス authorize (user_id, action, target_ids) => boolean

  45. 具体的利用例 エピソード無料公開期間の 作成を例として 処理を追いかける エピソード:第一話、第二話前半、単行本告知など ニコニコ漫画上での公開単位

  46. 具体例:無料公開期間を作成するとき Api::V1::FreeViewingTermsController#create

  47. 具体例:無料公開期間を作成するとき ざっくりとした各処理の区分 バリデーション 認可 DB書き込み ビューモデル & ビュー

  48. 具体例:無料公開期間を作成するとき ここで認可部を呼んでいる

  49. 認可部のエントリポイント authorize!がauthorizeを呼ぶ

  50. 認可部のエントリポイント authorize!がauthorizeを呼ぶ

  51. 認可部のエントリポイント target_idsが単体な場合もKernel.#Arrayでカバー

  52. 認可部のエントリポイント targetによって各Authorizerに分岐

  53. 認可部のエントリポイント targetの種類はactionから導ける

  54. 認可部のエントリポイント 無料公開期間のAuthorizerへ

  55. 無料公開期間のAuthorizer 各Authorizerの中でまたactionで分岐

  56. 無料公開期間のAuthorizer 各Authorizerの中でまたactionで分岐

  57. 無料公開期間のcreate権限判定

  58. 無料公開期間のcreate権限判定 まずEpisodeを引く

  59. 無料公開期間のcreate権限判定 Episodeを不足なく 取得できたか

  60. 無料公開期間のcreate権限判定 公式連載でない作品の エピソードが混ざって いないか

  61. 無料公開期間のcreate権限判定 権限付与の単位は作品なので エピソードが属するComicを Comic::OfficialComicとして引きなおす (ここ微妙ポイント)

  62. 無料公開期間のcreate権限判定 ユーザの権限情報を DBから引いてくる

  63. 無料公開期間のcreate権限判定 全作品対象の権限が あったら早期return

  64. 無料公開期間のcreate権限判定 権限の付与範囲ごとに 認可された作品を Arrayから取り除いていく

  65. 無料公開期間のcreate権限判定 最終的に空ならば 全て認可されている

  66. Authorizer.authorize!に戻ってきて 認可されたならばraiseされないので後続処理へ

  67. 以上が最も基本的な 認可の仕組み

  68. ところでDRYにはしたい 他のアクションと 判定基準が同じ場合も 当然ある

  69. 権限判定の移譲 アクションだけ差し替えて 移譲先Authorizerのauthorizeメソッドを呼ぶ

  70. ここで疑問 Actionごとに渡すべき IDの種類が違うのでは?

  71. Actionごとに渡すべきIDの種類が違う Actionごとに渡すべきIDの種類が違うのでは?

  72. Actionごとに渡すべきIDの種類が違う Actionごとに渡すべきIDの種類が違うのでは? 無料公開期間は エピソードに対して 作るのでepisode_id

  73. Actionごとに渡すべきIDの種類が違う Actionごとに渡すべきIDの種類が違うのでは? 無料公開期間は エピソードに対して 作るのでepisode_id 無料公開期間を消すときは free_viewing_term_id

  74. Actionごとに渡すべきIDの種類が違う Actionごとに渡すべきIDの種類が違うのでは? 無料公開期間は エピソードに対して 作るのでepisode_id 無料公開期間を消すときは free_viewing_term_id episode_id free_viewing_term_id free_viewing_term_id

  75. IDの種類が混ざらないのはなぜか 様々な種類のIDをどうやって 区別しているのか

  76. IDの種類を判別するしくみ • 各アクションのinterface class的なものを作る • 引数になりうるIDをインスタンス変数として持つ • attr_readerで読めるようにする • ID種別が非互換ならNo

    such methodで死ぬ • つまりダックタイピング
  77. 引数になるID種別が移譲先と違う場合 移譲先のID種別に詰め替える eql?とhashを実装すると uniqできる

  78. 未実装な機能 • ユーザがINDEXできるものだけINDEXする機能 • 現状、絞り込みのクエリパラメータが必ず付いて いるのでそれを起点に許可、拒否が決定できる • 現状、権限を持っている対象だけを絞ってINDEX したいという要求がまだないので困っていない •

    それが来たときにどう実現するかは要検討 • モデルのpublicメソッドにする? • どうにかしてActiveRecordスコープを作る? • CanCanCanのaccessible_byが参考になりそうか?
  79. まとめ • ニコニコ漫画の現行はモノリシックなPHP • ビジネスロジックのRails APIモードへのリプレイスが進行中 • 現行PHPはBFF的立ち位置に寄せていく • 認証と認可の定義

    • ニコニコ漫画では公式連載の原稿納入、公開期間設定などが認可の 対象 • Rubyの権限管理系gem 3種概観 • 検討した結果、いずれも使わなかった • ニコニコ漫画の認可機構はどのように実装をしているか • 無料公開期間の作成を例とした具体的流れ • 判定基準が同じアクションは判定を移譲している • 多数あるIDの種類をどうやって区別しているか • 今後の課題 • ユーザがINDEXできるものだけINDEXする機能
  80. 付録 ユーザの権限情報のDB上の表現 Watch: 作品・エピソードのINDEXとSHOW, 公開期間のINDEX Publish: 公開期間のCREATE, UPDATE, DESTROY Edit:

    作品・エピソードのUPDATE, DESTROY