くりにっき

フルスタックキュアエンジニアです

SAML連携したChatWorkアカウントでOAuthログインする方法

先日のブログの続編です

sue445.hatenablog.com

うちの会社ではChatWorkをSAML連携させて利用しています。

SAML連携したChatWorkアカウントだと普通のやり方ではOAuth認証できません。一応できるんですがやり方が難しすぎて初見だとエラーが出てハマるので現時点でのやり方を書いておきます。*1

やり方

  1. https://www.chatwork.com/login.php 経由でログインしてブラウザに予めChatWorkのセッションを作っておく。*2
  2. コンセント画面(omniauth-chatworkでは/auth/chatworkでリダイレクトした先の画面)では普通のログインボタンではなく、新規登録ボタンの真下の「KDDI ChatWorkをご利用の方はこちら」のリンク先からでログイン

このログイン画面だと普通赤いログインボタンを押すやろ・・・

f:id:sue445:20171116174813p:plain

omniauth-chatwork実装時にリファレンス通りにやってもOAuthログインできなくて、ChatWorkのサポートに問い合わせてメールで何往復かしてようやく上記の手順が分かりました。

一応断片的にはドキュメントには書いてあるんですが、非常に分かりづらいのでサポートに改善要望を投げています。

*1:最後に書いてるけど改善要望は投げているので今後仕様が変わる可能性はある

*2: http://developer.chatwork.com/ja/oauth.html の「OAuth2専用ログイン画面ではSAML認証には対応しておりません。通常のログイン画面を経由してSAML認証でログインした後、再度コンセント画面にアクセスしてください。」参照

omniauth-chatworkを作った

github.com

なんのgem?

最近ChatWorkでOAuth認証に対応したのですが、それに対応したomniauthのproviderです

使い方

OAuthクライアントの登録

まず最初に下記を参考にOAuthクライアントを作ってください

developer.chatwork.com

blog-ja.chatwork.com

この時リダイレクトURIhttps://〜/outh/chatwork の形式にしてください。

f:id:sue445:20171116165200p:plain

https://127.0.0.1:4567/ も登録しているのはローカルでの確認用です。

余談ですが2週間位前に https://localhost:4567/ で登録しようとすると「https://example.com形式で登録してください」的なエラーが出たのでその時は https://127.0.0.1:4567で登録して回避しました。

今確認したら https://localhost:4567/で登録できるようになっていたので今はどっちでもいいと思います。

omniauth-chatworkをproviderとして組み込む

あとは omniauth-facebookomniauth-twitter と同様に下記のような感じでproviderを登録してください

use OmniAuth::Builder do
  provider :chatwork, ENV["CLIENT_ID"], ENV["CLIENT_SECRET"]

  # scopeを明示的に指定したい場合はこっち
  provider :chatwork, ENV["CLIENT_ID"], ENV["CLIENT_SECRET"], scope: ["users.all:read", "rooms.all:read_write", "contacts.all:read_write"]
end

確認してないけどRailsなら config/initializers/omniauth.rb をこんな風に書いておけばたぶん動きます。

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :chatwork, ENV["CLIENT_ID"], ENV["CLIENT_SECRET"]
end

技術的なこと

ローカルでのSSL確認時はwebrick使うのが便利だった

http://developer.chatwork.com/ja/oauth.html にも書いてますがリダイレクトURIhttpsである必要があります。これは当然ローカルでの確認時にもhttpsが要求されます

ググるHTTPS証明書を作成してローカルで起動したApacheに喰わせて云々っていう記事が出ますが、Rubyならwebrickで下記のような設定を書いておけば起動時にオレオレ証明書を動的生成してくれるのでそんな心配はありません

set :server_settings,
    SSLEnable: true,
    SSLCertName: [["CN", WEBrick::Utils.getservername]]

https://github.com/sue445/omniauth-chatwork/blob/v0.1.0/spec/dummy/app.rb

実際ローカルでOAuth認証確認する時はomniauth-chatworkのリポジトリをcloneして bundle exec ruby spec/dummy/app.rb で動きます

参考資料

omniauth provider作成時に参考にしたもの

今までOAuth2のクライアントライブラリ使わずにゴリゴリ自前実装してきたので、omniauthのルールに則ってgemを作るってのが新鮮でした

追伸

SAML連携してるChatWorkアカウントだとOAuth認証が非常に分かりづらいのでそれはまた後日書きます

-> 書いた

sue445.hatenablog.com

wercker_build_triggerを作った

github.com

wercker_build_triggerについて

Wercker のビルドを外部から手軽に実行するためのツールです

モチベーション

僕は下記のように週1回Travis CIで定期ビルドを実行しています

sue445.hatenablog.com

WerckerでCI回してるリポジトリでも定期ビルドをやりたかったのですが、WerckerではTravis CIのCron jobsのような機能がなかったのでcrontabから実行しやすくするためにツールを作りました。

使い方

golang製なので https://github.com/sue445/wercker_build_trigger/releases からバイナリを落としてきて適当な場所に置いてください。

設定ファイルはこんな感じ

pipelines:
  - application_path: "wercker/docs"
    pipeline_name: "build"
    branch: "master"
  - application_path: "sue445/wercker_build_trigger"

application_pathは必須。pipeline_namebranchは省略可。pipeline_name省略時はbuildが、branch省略時はmaster が使われます

crontabにこんな感じに書いておけば毎週日曜の3:00に実行されます。

0 3 * * 0 /path/to/wercker_build_trigger --config /path/to/wercker_build_trigger.yml --token xxxxxxx

tokenは https://app.wercker.com/profile/tokens で生成したやつを貼り付けてください。

導入事例

実際に使ってる設定ファイル。

pipelines:
  - application_path: "sue445/itamae-plugin-recipe-omori_gohan"
  - application_path: "sue445/itamae-plugin-recipe-tmux"
  - application_path: "sue445/itamae-plugin-recipe-tig"
  - application_path: "sue445/itamae-plugin-recipe-git_now"
  - application_path: "sue445/itamae-plugin-resource-encrypted_remote_file"
  - application_path: "sue445/itamae-plugin-recipe-consul"
  - application_path: "sue445/capistrano-itamae"
  - application_path: "sue445/wercker_build_trigger"

wercker_build_trigger自身の定期ビルドもwercker_build_triggerで行っています。(ややこしい)

実行結果

[application_path:sue445/itamae-plugin-recipe-omori_gohan][pipeline_name:build][branch:master] Triggered pipeline: https://app.wercker.com/api/v3/runs/59d9a54714816100010bd46f
[application_path:sue445/itamae-plugin-recipe-tmux][pipeline_name:build][branch:master] Triggered pipeline: https://app.wercker.com/api/v3/runs/59d9a54814816100010bd477
[application_path:sue445/itamae-plugin-recipe-tig][pipeline_name:build][branch:master] Triggered pipeline: https://app.wercker.com/api/v3/runs/59d9a5492535040001c453c2
[application_path:sue445/itamae-plugin-recipe-git_now][pipeline_name:build][branch:master] Triggered pipeline: https://app.wercker.com/api/v3/runs/59d9a54a8e888f0001b7de36
[application_path:sue445/itamae-plugin-resource-encrypted_remote_file][pipeline_name:build][branch:master] Triggered pipeline: https://app.wercker.com/api/v3/runs/59d9a54c8e888f0001b7de3e
[application_path:sue445/itamae-plugin-recipe-consul][pipeline_name:build][branch:master] Triggered pipeline: https://app.wercker.com/api/v3/runs/59d9a54d14816100010bd47f
[application_path:sue445/capistrano-itamae][pipeline_name:build][branch:master] Triggered pipeline: https://app.wercker.com/api/v3/runs/59d9a54e8e888f0001b7de46
[application_path:sue445/wercker_build_trigger][pipeline_name:build][branch:master] Triggered pipeline: https://app.wercker.com/api/v3/runs/59d9a54f2535040001c453ca

ビルド結果はSlackに流すようにしてるのでこんな風に通知されます

f:id:sue445:20171008131301p:plain

DigitalOceanで使えるimageを一覧で取得する

tl;dr;

https://gist.github.com/sue445/dd1ab749f9cf364777acbe66d34576e4

前置き

itamaeプラグインのCIを回してたら DigitalOceanで「centos-7-0-x64なんてイメージはないぞ!」ってエラーになりました

+ vagrant up centos70 --provider=digital_ocean
Bringing machine 'centos70' up with 'digital_ocean' provider...
==> centos70: Using existing SSH key: wercker-itamae-plugin-recipe-consul
There was an issue with the request made to the DigitalOcean
API at:

Path: /v2/droplets
URI Params: {:size=>"512MB", :region=>"nyc3", :image=>"centos-7-0-x64", :name=>"itamae-consul-centos70-59d3b5938e888f0001b27d75", :ssh_keys=>[1874082], :private_networking=>false, :ipv6=>false}

The response status from the API was:

Status: 422
Response: {"id"=>"unprocessable_entity", "message"=>"You specified an invalid image for Droplet creation."}

Vagrabtfileだと下記のように provider.image で設定してたのですが、どうも消えた模様。

  config.vm.define :centos70 do |c|
    c.vm.box = "centos/7"
    c.vm.provider :digital_ocean do |provider, override|
      provider.image = "centos-7-0-x64"
    end
    c.vm.hostname  = 'itamae-consul-centos70'
    c.vm.hostname  += "-#{ENV['WERCKER_RUN_ID']}" if ENV['WERCKER_RUN_ID']
  end

実際に使えるイメージ一覧を自分のgistには乗っけてはいたんですが、ググれる場所にあった方が便利なのでブログに記載

使い方

はじめに https://cloud.digitalocean.com/settings/api/tokensAPIトークンを取得

f:id:sue445:20171004203142p:plain

あとはこんな感じに実行したらjsonがとれます。下記のslugをVagrantfileに渡してやればよさそう

$ export DIGITALOCEAN_ACCESS_TOKEN="xxxxxxxxxxxxxxx"
$ curl -s "https://api.digitalocean.com/v2/images?filter=global&per_page=100" -H "Authorization: Bearer $DIGITALOCEAN_ACCESS_TOKEN" | jq .

{
  "images": [
    {
      "id": 27983450,
      "name": "1520.4.0 (beta)",
      "distribution": "CoreOS",
      "slug": "coreos-beta",
      "public": true,
      "regions": [
        "nyc1",
        "sfo1",
        "nyc2",
        "ams2",
        "sgp1",
        "lon1",
        "nyc3",
        "ams3",
        "fra1",
        "tor1",
        "sfo2",
        "blr1"
      ],

slugで検索したい場合はこんな感じ

$ curl -s "https://api.digitalocean.com/v2/images?filter=global&per_page=100" -H "Authorization: Bearer $DIGITALOCEAN_ACCESS_TOKEN" | jq -r ".images[].slug" | grep centos
centos-6-5-x32
centos-6-5-x64
centos-6-x32
centos-6-x64
centos-7-x64

jsonはここに置いてるのでページ内検索でも探せます

https://gist.github.com/sue445/dd1ab749f9cf364777acbe66d34576e4

gitlab_mr_release v1.0.0をリリースした

github.com

ふと GitLab APIのドキュメント を読んでたら GET /projects/:id/merge_requests で複数のiidを渡してまとめてMRを取得できるようになってることに気づき、(APIの実行回数的な)N+1問題を解決できそうな気がして改修。 *1

あと、この辺の仕様がAPI v3とv4で微妙に違っていて両方対応するのも面倒だったのでv3を切り捨ててv4のみで動くようにしてます。

v3で動かそうとすると下記のようなエラーになるので注意

$ gitlab_mr_release create
GitlabMrRelease::InvalidApiVersionError: 'https://gitlab.com/api/v3' seems not to be GitLab API v4+. gitlab_mr_release requires GitLab API v4+

詳しくは https://github.com/sue445/gitlab_mr_release/blob/master/CHANGELOG.md 読んで下さい

*1:gitlab_mr_releaseを作った当時はiidは1つずつしか渡せなかった気がする

CIのバッジを並べて表示するだけのサイトを作った

https://sue445.github.io/my-ci-badges

github.com

モチベーション

gemやアプリなどを40個くらいメンテしてて一応全てでweekly ビルドを実行してるのですが、CIのバッジが各リポジトリにしか貼っていなくてCIのステータスをまとめて見るためのポータルサイトが欲しかったので作りました。*1

製作時間半日くらい

f:id:sue445:20170917172732p:plain

使ってる技術

  • Bootstrap v4.0.0-beta
    • いつものようにbootstrap使おうと思ったら4系が出てたので雑に投入
  • Vue.js
    • 「いつもMithril.js を使ってるのでたまには違うのを使うか」っていう雑な理由でVue.jsを採用

デプロイ周り

GitHub Pagesを使ってるのでGitHubにpushするだけ。CIいらずで楽

*1:ビルド結果も個人Slackに全部流してるんだけどさすがに40個同時だとログが流れて気づかないことがたまにある

tweet_sanitizerというgemを作った

退院後の初gem情報です

github.com

どんなgemか?

Twitter APIで取得したツイートの内容から

  • full_text (140文字より長いツイート)があればそれを積極的に使う
  • 短縮URLの t.co を元のURLに戻す
  • ツイートの末尾に含まれてる添付画像などのURLを除去
  • ><&gt;&lt; などにエスケープされているので、エスケープを解除

などのサニタイズ処理を行います

モチベーション

Twittodon のロジックをgem化しています

sue445.hatenablog.com

twittodonのツイートのサニタイズ周りの処理にバグがあったんですが、

という理由でgem化してテストコードをリファクタリングしつつバグ修正しました *1

使い方

tweet_sanitizerを使わない場合

普通に twitter のgemを使った場合

tweet = @client.status("https://twitter.com/github/status/866677968608927744")
tweet.text
#=> "Introducing GitHub Marketplace, a new place to browse and buy integrations using your GitHub account.… https://t.co/dK0Tmcmm72"

tweet_sanitizerを使った場合

using TweetSanitizer::TwitterExtension

extended_tweet = @client.status("https://twitter.com/github/status/866677968608927744", tweet_mode: "extended")
extended_tweet.sanitized_text
#=> "Introducing GitHub Marketplace, a new place to browse and buy integrations using your GitHub account. https://github.com/blog/2359-introducing-github-marketplace-and-more-tools-to-customize-your-workflow"

using TweetSanitizer::TwitterExtension すると生える #sanitized_text でいい感じにサニタイズできます

ツイート取得時の tweet_mode: "extended" がないと前述の full_text がレスポンスで返ってこないので必須です

#status メソッド以外でもツイートを取得しそうなAPI#search とか)のオプションに tweet_mode: "extended" を渡せばたいてい対応してます

参考

Upcoming changes to Tweets — Twitter Developers

詳しくはREADME読んでください

ユースケース

APIで取得したツイートをmastodonなど他のツールに流したり、DBに保存したいような場合に便利かもしれません