Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

自己紹介 • 名前:フサギコ • Twitter :fusagiko • GitHub :takayamaki • 本名 :髙﨑 尚人 • 主な分野:Ruby(Rails), Terraform(AWS) • 他に :TypeScript, React, Ansible • Ruby:2.1.2~ • 大学院生の時に修士論文用のシミュレータを Pure Rubyフルスクラッチで書いたのが最初 • 趣味:アイドルマスター • 全作品、それぞれ楽しんでるタイプ

Slide 3

Slide 3 text

自己紹介(OSS活動) • マストドン • 本家(tootsuite/mastodon)コントリビュータ • 2年弱PR送れてないけどまだcommit数37位らしい • imastodon.net構築、運用、管理人 • アイドルマスターのファン向け非公式マストドン • 2017年4月開設 • アカウント数4817、アクティブ数500/週程度 • 本家に独自改造が入っている • 完全に袂を分かったわけではないのでVariety(変種)と自称 • 追従は遅れ気味…

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

ニコニコ漫画 ニコニコ漫画

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

その前に と、その前に

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

認証と認可 認証 authentication 認可 authorization “それ”が誰であるかを 確かめること to prove that something is real “それ”に許可を 与えること to give permission for something

Slide 22

Slide 22 text

このように 認可と認証は 異なる行為

Slide 23

Slide 23 text

認証と認可 認可 しない する 認 証 し な い 何もわからないし 何もさせない 誰であるかは 自己申告を信じ、 操作を許可する す る それが誰かわかるが、 何もさせない それが誰かを識別し、 誰であるかによって 操作を許可する

Slide 24

Slide 24 text

認可 と 認証が間違われるのは 全部TwitterのOAuthが悪い いわゆるログイン連携がしたいだけなら 認証(”それ”の識別)さえできれば良かったはずなのに

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

CanCanCan • ユーザを中心に、何へどのアクションを認可するか DSL的に記述 • can 【アクション】, 【対象】, 【各種条件】 • 対象にはActive Record継承したClassも指定できる • クエリインターフェイスに統合した形で使えて便利 • Active Recordに関係ない対象はシンボルで表現 • can? 【アクション】, 【対象】で判定 • => Boolean • 対象がActive Recordモデルの場合 • Article.accessible_by(current_ability) とかできる

Slide 33

Slide 33 text

Pundit • 認可系gemとしてはおそらくもっとも有名か • モデルを中心に、誰にどのアクションを 認可するか記述 • ActiveRecordモデルに対する操作タイミングで認 可を行う • 複雑になってきたRailsアプリケーションではモ デルとコントローラが一対一ではなくなりがち • コントローラごとに認可条件を変えたい場合詰む

Slide 34

Slide 34 text

Banken • コントローラのアクションメソッドと認可条件 を紐づける • アクションを中心に、誰に許可するか記述 • コントローラのアクションメソッドと対応付ける ので対象リソースは自ずと導き出せるはず

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

ニコニコ漫画は 権限判定に どのgemを使ったか

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

どの認可系gemも使わなかった • 現行PHPとDBを共有しながらの順次リプレイス • 現行が不完全なレベルベースの認可 • モデルと必要権限が一対一で対応しない • 特定カラムの更新だけ上位権限が要る場合がある • アクションメソッドと必要権限も対応しない • 「このカラムの更新には上位権限が要る」といった知識を BFFに流出させてはならない • バルク(一括)操作がある • 唯一可能性があったのはCanCanCanだが… • どう考えても大規模になるのでメンテしづらそう • ユーザの権限情報のDB上の表現からCanCanCanの DSLに落とし込める確信が持てなかった

Slide 39

Slide 39 text

gemを使わない…というか使えない 前述の通り、 新ニコニコ漫画は 既にRails的でない部分がある

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

引数になるID種別が移譲先と違う場合 移譲先のID種別に詰め替える eql?とhashを実装すると uniqできる

Slide 78

Slide 78 text

未実装な機能 • ユーザがINDEXできるものだけINDEXする機能 • 現状、絞り込みのクエリパラメータが必ず付いて いるのでそれを起点に許可、拒否が決定できる • 現状、権限を持っている対象だけを絞ってINDEX したいという要求がまだないので困っていない • それが来たときにどう実現するかは要検討 • モデルのpublicメソッドにする? • どうにかしてActiveRecordスコープを作る? • CanCanCanのaccessible_byが参考になりそうか?

Slide 79

Slide 79 text

まとめ • ニコニコ漫画の現行はモノリシックなPHP • ビジネスロジックのRails APIモードへのリプレイスが進行中 • 現行PHPはBFF的立ち位置に寄せていく • 認証と認可の定義 • ニコニコ漫画では公式連載の原稿納入、公開期間設定などが認可の 対象 • Rubyの権限管理系gem 3種概観 • 検討した結果、いずれも使わなかった • ニコニコ漫画の認可機構はどのように実装をしているか • 無料公開期間の作成を例とした具体的流れ • 判定基準が同じアクションは判定を移譲している • 多数あるIDの種類をどうやって区別しているか • 今後の課題 • ユーザがINDEXできるものだけINDEXする機能

Slide 80

Slide 80 text

付録 ユーザの権限情報のDB上の表現 Watch: 作品・エピソードのINDEXとSHOW, 公開期間のINDEX Publish: 公開期間のCREATE, UPDATE, DESTROY Edit: 作品・エピソードのUPDATE, DESTROY