Gitでアリバイ工作するスクリプトはコチラです
[Defi] Master and Servant by Franck Mahon, on Flickr
あなたはとある事件の容疑者だ。 悪いことに、事件当日の居場所を証明できないでいる。
というか、ここだけの話ホントはアナタが真犯人。だからこの際アリバイ工作するしか無いようだ・・・
そこであなたは、Gitのコミット日時を書き換える(え?)・・・
(という設定で以下進行ってなんだそりゃ?)
コミット日時を偽装する
ってな時に、Gitのコミット日時を偽装して、簡単にアリバイ工作(?)できるスクリプトが、これ(↓)ですね。
git-giso
#!/bin/sh if [ "$1" = "" ]; then echo "Error: datetime need to be specified" exit 1 fi echo git commit --amend -C HEAD --date="$1" && \ git commit --amend -C HEAD --date="$1" && \ echo && \ echo git rebase HEAD~ --committer-date-is-author-date && \ git rebase HEAD~ --committer-date-is-author-date
このファイルに実行属性を付けてPATHの通った所へINSTALL。
実行例
実行すると以下のようになりますよ(極秘情報は伏せてある)。
$ git-giso '2018-02-17 20:26:45' git commit --amend -C HEAD --date='2018-02-17 20:26:45' [working-branch abcdefg] #$%S&Gys7&%$# Date: #$$ Feb %& $%#2#$$%&'(&%%% n file changed, 4 insertions(+) git rebase HEAD~ --committer-date-is-author-date Current branch working-branch is up to date, rebase forced. First, rewinding head to replay your work on top of it... Applying: %&%S$%S&&D'F/??./
それは、ヒ・ミ・ツ
理由は明かせないけれど、コミット日時を書き換えたいときがあります(よね?)。あるんです。
絡み合う2つの日時・・・
- Gitのコミットには
author-date
とcommitter-date
という2つの日付がありまして、最初にコミットした時、両方同時刻に設定されます。 - でも、
git commit --amend
した時には、committer-date
しか書き換わらないの・・・ - てことで、
git commit --amend -C HEAD --date='<datetime>'
としてauthor-date
を書き換える。 - しかし、
committer-date
が、その時の時刻になってしまっているので、 git rebase HEAD~ --committer-date-is-author-date
でcommitter-date
をauthor-date
に一致させるというわけです。
ふう・・・
masterブランチでやっちゃダメ
これ、amend
してrebase
するので、ワーキングブランチでやりましょう。
master
ブランチでコレやって、push -f
とかしちゃったら、市中引き回しの上獄門ですから気をつけて。
なんで縄文・・・
DynamoDBをSQLで操作するNodeモジュール
Amazon DynamoDBをSQL的な記述言語(SQL-ish)で操作できるNodeモジュールのご紹介。
目次
概要
このモジュールからはDynamoDBのテーブルをSQL的な文(SQL-ish)で操作できるステートメントを提供しています。
ステートメントを生成する関数は以下の4つ:
- awsNodeUtil.dynamodb.ScanStatement - テーブルをスキャンします。
- awsNodeUtil.dynamodb.QueryStatement - テーブルをクエリします。
- awsNodeUtil.dynamodb.PutItemStatement - 項目を追加、またはキーを指定して上書きできます。
- awsNodeUtil.dynamodb.DeleteItemStatement - キーを指定して項目を削除。
それぞれ、引数にSQL(によく似た)文を与えてステートメントを生成します。 各ステートメントは後からパラメータを与えて実行可能(※ただしDeleteItemStatementは現在パラメタライズが出来ません)。
このほか、ScanとQueryの結果の中の Itemsを(DynamoDBのマップ型の配列から)普通のオブジェクトの配列に変換する DynamoDbResultSet クラスの getItemsメソッドが使えます。
また、v0.9.6では、 PutItemStatement が返す DynamoDBPutItemStatementクラス の setValuesメソッドで、新たなVALUESを設定してステートメントを実行できます。
以下にScanとQueryの簡単なサンプルコードを掲載してます。 他のステートメントのサンプルやAPIの詳細はAPIリファレンスを参照してくださいね。
SQL-ish Statement クラスの使用例
実際に動作させるには、DynamoDBにstarsというテーブルがあり、HASHキーがString型のmainStar、RANGEキーがNumber型のorbitOrderとなっている必要があります。
"use strict"; const awsNodeUtil = require("aws-node-util"); const ScanStatement = awsNodeUtil.dynamodb.ScanStatement; const QueryStatement = awsNodeUtil.dynamodb.QueryStatement; const ResultSet = awsNodeUtil.dynamodb.ResultSet; // Connect (change each value for your account) awsNodeUtil.dynamodb.connect( // { accessKeyId: 'AKID', secretAccessKey: 'SECRET', region: 'us-west-2' } ); // Prepare 'Scan' statement var scanStatement = ScanStatement( "FROM stars WHERE name=:name"); // Prepare 'Query' statement var queryStatement = QueryStatement( "SELECT mainStar, orbitOrder, name " + "FROM stars " + "WHERE mainStar=:mainStar"); // Run the statements ScanStatement("FROM stars").run({}, (err, resp) => { console.log("-----------------------"); console.log("SCAN all items of stars"); console.log("-----------------------"); printResult(err, resp); scanStatement.run({ ":name": "EARTH" }, (err, resp) => { console.log("--------------"); console.log("SCAN the EARTH"); console.log("--------------"); printResult(err, resp); queryStatement.run({ ":mainStar": "EARTH" }, (err, resp) => { console.log("------------------------------"); console.log("QUERY child stars of the EARTH"); console.log("------------------------------"); printResult(err, resp); }); }); }); // Handler to print result of scan / query function printResult(err, result) { if(err) { console.error("Error:", err.stack); } else { ResultSet.printScanResult(result); } }
実行時の出力例:
$ node sample/sqlish-sample.js ----------------------- SCAN all items of stars ----------------------- Count: 10 ROWNUM diameter rotation role mass gravity density escapeVelocity name orbitOrder mainStar 1 3475 655.7 satellite 0.0073 1.6 3340 2.4 MOON 1 EARTH 2 4879 1407.6 planet 0.33 3.7 5427 4.3 MERCURY 1 SUN 3 12104 -5832.0 planet 4.87 8.9 5243 10.4 VENUS 2 SUN 4 12756 23.9 planet 5.97 9.8 5514 11.2 EARTH 3 SUN 5 6792 24.6 planet 0.642 3.7 3933 5.0 MARS 4 SUN 6 142984 9.9 planet 1898.0 23.1 1326 59.5 JUPITER 5 SUN 7 120536 10.7 planet 568.0 9.0 687 35.5 SATURN 6 SUN 8 51118 -17.2 planet 86.8 8.7 1271 21.3 URANUS 7 SUN 9 49528 16.1 planet 102.0 11.0 1638 23.5 NEPTUNE 8 SUN 10 2370 -153.3 planet 0.0146 0.7 2095 1.3 PLUTO 9 SUN ScannedCount: 10 -------------- SCAN the EARTH -------------- Count: 10 ROWNUM diameter rotation role mass gravity density escapeVelocity name orbitOrder mainStar 1 12756 23.9 planet 5.97 9.8 5514 11.2 EARTH 3 SUN ScannedCount: 10 ------------------------------ QUERY child stars of the EARTH ------------------------------ Count: 1 ROWNUM name orbitOrder mainStar 1 MOON 1 EARTH ScannedCount: 1
SQL-ishの構文
各Statementの構文を以下にざっくり説明します。
[
~ ]
は省略可能を意味します。<
~ >
は後述します。
/* Scan */ [SELECT <projection-expression>] FROM <table-name> [WHERE <filter-expression>] [LIMIT <limit>] /*Query*/ [SELECT <projection-expression>] FROM <table-name> WHERE <key-condition-expression> [FILTER <filter-expression>] [LIMIT <limit>] /*PutItem*/ INSERT INTO <table-name> ( <attribute-list> ) VALUES ( <value-list> ) [WHERE <key-condition-expression>] /*DeleteItem*/ DELETE FROM <table-name> [WHERE <key-condition-expression>]
SELECT句
SELECT句には<projection-expression>
にカンマ区切りで選択する属性名を指定します。
通常のSQLと違ってSELECT句は省略可能です。省略時は全属性が選択されます。
(「属性」は一般的なDBでは列、またはカラムに相当します)
FROM句
FROM句は必須です。
<table-name>
に検索を行うテーブル名を指定します。JOINなどは出来ません。
WHERE句
WHERE句はScanとその他のステートメントでは意味が違います。
ScanStatementでは、フィルター条件式(<filter-expression>
)を指定します。これは省略可能です。省略すると全件取得。
フィルタ条件式にはキー属性を記述できません。これが必要な場合はQueryを使用します。
他のステートメントではキーの条件式(<key-condition-expression>
)を指定します。
QueryStatementでは必ず記述しなければなりません。
キーの条件式には、キー以外の属性を記述できません。
QueryStatementでキー以外の属性で絞り込みたい時はFILTER句を使用します。
その他、キーの条件式には、以下のような制限もあります。これらは全てDynamoDBのKeyConditionExpressionの制限です。
- パーティションキーの条件は必須で、
=
で一致させなくてはなりません。 - ソートキーの条件をANDで追加できます。この条件式で使用できる比較演算子は
=
,<
,<=
,>
,>=
,BETWEEN ~ AND
だけです。IN
は使用できません。関数はbegins_with
だけ使用できます。 - 詳細はQuery - Amazon DynamoDB(英文)に書いてあります。
FILTER句
FILTER句は、QueryStatementにフィルタを設定するために記述します。 省略可能。省略時はフィルタはかかりません。
ScanStatementにFILTER句は指定できません(WHERE句に指定)。
LIMIT句
LIMIT句は<limit>
にScanする件数(数値)を指定します。
省略可能ですが注意が必要。
LIMIT句を省略するとScanStatementではWHERE句(フィルタ)の指定に関わらず、全件をスキャンすることになります。 QueryStatementでは必ずパーティションキーの条件が入っていますのである程度件数は絞られます。 いずれにせよ項目数の多いテーブルでLIMIT句を省略すると処理速度や料金に影響しますので注意して下さい。 これはDynamoDBの仕様です。
VALUES句
PutItemStatementのVALUES句は、( <attribute-list> ) VALUES ( <attribute-list> )
という形式で、
追加・上書きする項目の属性(RDBでのカラム名に相当)と値をそれぞれカンマ区切りで並べたものです。
属性と値の数は一致しなければなりません。
各値の型は表記から自動的に判断されます。
プレースホルダーについて
DynamoDBを使う上でちょっと邪魔くさい「名前のプレースホルダー」、「値のプレースホルダー」について気にする必要はありません。 本モジュール内で構文解析時に自動的に識別して変換します。
ステートメントのパラメタライズ
上の例に示しているように、パラメータ化する箇所には半角コロン:
で始まる識別子を直接記述しておけば、実行時にパラメータを与えて処理できるようになります。
DynamoDBについての基本事項
DynamoDBはNoSQL
DynamoDBはAWS ― Amazon Web Service ― のNoSQL型データベース。 リレーショナルではありませんので複数テーブルの突き合わせ(JOIN)とかは出来ません。
DynamoDBのScanとQueryの違いについて
ScanもQueryもDynamoDBのテーブルから項目を読み込む機能ですが、読み込み方に違いがあります。
Scanはキーによる絞り込みが出来なくて、項目の並び順に順次読み込み(=スキャン)する感じ。 Queryはスキャンの前にキーの条件で絞り込めます。
どちらもスキャンしたあとに「フィルター」をかけられ、スキャンする項目数を指定できます。 また、続きから読むという操作も可能です。
あとがき
DQLというPython製ですが似たコンセプトのモジュールがあるらしいですね。 使ったことがないのでよくわからないですが、シェルから実行できる対話式のクライアントという感じでしょうか。
当npmをリリースした後「似たようなのって既にあるんじゃないの?」って検索して知ったのでワタシは無罪。 しかし、こちらが後発なのは明らかなので今後構文的にDQLに寄せていくのもありかなと思っています。BNFもドキュメントで規定されているし。
package-lock.jsonの潜在的セキュリティ脆弱性を解消しました
photo credit: wuestenigel blue padlock via photopin (license)
昨年12月末、GitHubに置いてる自作npmのリポジトリに「潜在的なセキュリティの脆弱性がありますよ」ってメッセージが表示されるようになって、 「公開しているnpmのリポジトリに、こんなの表示されたらかなわんなあ」と思いながらも、 なんか怖いし対処法も分からんし、2週間ぐらいは見て見ぬふりしていましたが、この度正しく対処してメデタク解消致しました。
結果的には大したことはしていませんが記録として書いておきます。
関連する最新記事(↓): なんとワンクリックで対処できるようになってます。 takamints.hatenablog.jp
潜在的セキュリティ脆弱性があるらしい
何やら怪しげなメッセージは、コチラ(↓)です。
”potential security vulnerabilities”が「潜在的セキュリティ脆弱性」。
文章部分を、ちゃんと翻訳すると以下のようになりますね。
We found potential security vulnerabilities in your dependencies. Some of the dependencies defined in your package-lock.json have known security vulnerabilities and should be updated. Only the owner of this repository can see this message.
「わたしたちは、あなたの依存パッケージに潜在的なセキュリティ脆弱性の可能性を見つけました。 あなたの package-lock.json 内の依存パッケージのいくつかは、既知のセキュリティ脆弱性があり、これらは更新されるべきです。 リポジトリのオーナーだけがこのメッセージを見ることができます。
最後の一文でホッとしましたが、どうすりゃいいかはわからない。 とりあえず「Review Vulnerable dependencies」ボタンを押せば、以下(↓)の詳細が表示されます。
このページ、昨年末にGitHubに追加された機能ですね。「早速動いているんだな」と当たり前のことに感心しました。
それはさておき、どうやら markedの 0.3.6
に脆弱性があるようです。
そして、小さなドロップダウンを開くと、修正された 0.3.9
以降に更新すればいいよと言ってるようだ。
ただし自作パッケージではmarkedを使用していません。依存パッケージのどれかが依存しているのでしょう。
package-lock.jsonを編集するのはよろしくないかも
最初は単純に package-lock.json に記述されてる marked のバージョンを 0.3.9
に書き換えればよいと思いましたが、そうでもなさそう。
セマンティックバージョニングを厳格に適用するなら、以下の理由によって正しく動作する保証がありません。
- marked に依存しているパッケージは
marked@0.3.9
で動かされた実績がありません(パッチレベルのバージョンアップですので実際には動くかもしれませんが、それでも確証はありません)。 - markedのメジャーバージョンは0(=正式リリースされていない状態)なので互換性が保証されない可能性があります。
- package-lock.json は古いバージョンに固定したい(勝手に新しいバージョンが使われないようにする)時に使うものだと思ってます(あってますかね?)ので、新しいバージョンに固定するのは気持ち悪い。
ということから、直接 marked に依存しているパッケージを更新し、marked@0.3.9
以上に依存するのが安全ぽいという結論に。
どいつが依存しているんだ?
しかし、package-lock.json を開いても、どのパッケージがmarkedに依存しているかという情報はないのですね。
npmの依存関係は npm ls
で全部表示できるのですが、恥ずかしながらこの時スッカリ忘れていまして、findとgrepで node_modules以下にインストールされた全パッケージのpackage.jsonからmarkedをゴリゴリ検索しちゃいました。これ結構時間がかかりますので良い子は真似しない。npm ls
はnode_module以下を見ずに依存ツリーを表示してくれるのでこちらが標準。
$ find . -name 'package.json' -exec egrep -H '^\s*"marked"' {} \; ./express/package.json: "marked": "0.3.6", ./jsdoc/package.json: "marked": "~0.3.6", ./marked/package.json: "marked": "./bin/marked"
はい出ました。expressとjsdocでした(最後の行はmarked自身)。
あとは更新してプッシュ
どちらも、npmで確認すると新バージョンが出ておりまして、それぞれ修正済みのmarkedへ依存していました。 そこで、この2つのパッケージを最新版に更新し、package-lock.json も更新し、 GitHub へアップすれば一件落着。
Push後すぐにはメッセージは消えませんでしたが、少なくとも1時間後には消えていました。
まとめ
- ちょっと邪魔くさかったけど、定期的に npm-check-updates してればこういう目に合わなくてよいのかも知れないなと。
- npm 5で追加されたpackage-lock.jsonについて、きちんと理解する必要があると痛感しました。
WindowsでSwift使う(iPhone開発とは言ってない)
Swift For Windowsをインストールすればヨシ。
デフォルトで C:\Swift
にインストールされる。
GUIから使うにはこのままで良いのだけれど、できればCLIから使いたい。
コマンドプロンプトやPowerShellから使うには以下の4つにPATHを通す。
C:\Swift\mingw64\bin; C:\Swift\wxWidgets-3.0.3\lib\gcc510TDM_x64_dll; C:\Swift\usr\lib\swift\mingw; C:\Swift\usr\bin;
これで難なくコンパイルできるし実行モジュールもダブルクリックで実行可能。GitBashからもOKですね。
しかしESP-IDF用にインストールしたMSYS2からは使えなかった(なんでか知らん)。
そこで、上記とほぼおなじことだけど、同MSYS2の .bash_profile
に以下の4行を追加する。
PATH="${PATH}:/c/Swift/mingw64/bin" PATH="${PATH}:/c/Swift/wxWidgets-3.0.3/lib/gcc510TDM_x64_dll" PATH="${PATH}:/c/Swift/usr/lib/swift/mingw" PATH="${PATH}:/c/Swift/usr/
これでめでたくいたるところでSwiftをコンパイルできて実行可能。おめでたい。
まあ、iPhoneアプリは開発できないけど、言語は習得できますね。
参考リンク:
コンソールからGoogle OAuth2の認証を行う
photo credit: suzyhazelwood DSC04299-02 via photopin (license)
コンソールから Node.jsを使って Google OAuth2 クライアントID による OAuth 認証のお試しコードを書いてみました。
日々の作業でGit BashやMSYS2を多用していますが、コンソールからGoogle Driveのスプレッドシートを参照したり、検索、定型データの追加などが軽くできれば、いちいちブラウザアプリを開いて「よっこらしょ」ってな感じより効率的な場合もあるかと思い、これをベースに「なんか作るか」と、思ったり思わなかったりして、いくつになってもお勉強です。
売り上げランキング: 1,632
目次
- Google OAuth2 に必要なもの
- そもそも OAuth の認証・認可とは
- 認可を求めるのは1度だけ
- 認可コード取得を自動化
- プロジェクトとクライアントIDを作成する
- サンプルコード
- 依存モジュールのインストール
- 実行例
- まとめ
- 参考サイト
関連記事:
Google OAuth2 に必要なもの
ここで示している認証では Google Developper ConsoleのAPIプロジェクト(=アプリケーション)に作成する「クライアントID」と「クライアントシークレット」を使用します(作成手順は後述します)。
これとは別に「クライアントシークレット」を使わず「API KEY」と「クライアントID」を使う方法もあるようですが、Node.js からできるかどうかわかりませんでした。
そもそも OAuth の認証・認可とは
このアプリケーションは、Google Drive API を使用して、ユーザー自身の Google Drive のファイルにアクセスします(とプロジェクトに設定している)。しかしユーザーファイルを勝手に触るわけにはいきません。 このため、実行時に認証ページを表示して、「このアプリケーションがユーザーファイルにアクセスして良い」と、ユーザーに認可してもらうのです。
ユーザーが認可すれば、認可コードがアプリケーションに通知され、アプリケーションは実際にユーザーデータにアクセスするためのアクセストークンを手に入れます。
認可を求めるのは1度だけ
認可を求める認証ページが開くのは初回の認可コードを取得するときだけです。
アクセストークンには有効期限が定められていますが、新たなアクセストークンを取得するためのリフレッシュトークンも含まれており、2回目以降は、これを使って新しいアクセストークンを取得できるというわけ。
(掲載しているプログラムでは、有効期限の判定は一切行わず、常にリフレッシュしています)
認可コード取得を自動化
初回の認証シーケンス(認証ページの表示から認可コードの取得まで)は一般的なウェブアプリと同じように自動化しています。 通常のサンプルコード等では「以下のURLをブラウザで表示して、表示された認証コードを入力してね」なコピペな感じですが、それよか格段に楽ですよ。
認可コードを得る部分のフローは。
- プロセス内にローカルウェブサーバーを起動。
- 認証ページをユーザーのデフォルトブラウザで表示。
- 認証時のリダイレクト先を上記ウェブサーバーのURLに設定。
- ユーザーが認証を完了させると、認可コードがリダイレクト。
認可コードはHTTP GET REQUEST の QUERY_STRINGに含まれています。 だからウェブサーバーがリクエストを受け付けた時点で認可コードを取得できるというわけです。
プロジェクトとクライアントIDを作成する
以下の手順に従って、Google Developper ConsoleのプロジェクトとクライアントIDを作成し、クライアントIDのキーファイルをダウンロードしておく必要があります。
- Google Developper Consoleで、新規プロジェクトを作成し、上のドロップダウンから作成したプロジェクトを選択。
- 「ライブラリ」タブで「Google Drive API」を使用できるように設定しておきます。検索ボックスに「Drive」と入力すればすぐ見つかる。認証をおこなうだけなら不要かもしれませんが、サンプルプログラムではGoogle Driveのルートにあるファイルの一覧を表示するために必要です。
- 「認証情報」タブで「OAuth 2.0 クライアント ID」を新規作成(「アプリケーションの種類」は「ウェブアプリケーション」か「その他」を選択。
- 作成したクライアントIDのJSONファイルをダウンロードします。
サンプルコード
以下のコードは、依存モジュールをインストールすれば単体で動作します。
ちょっと(いやかなり)長くなってしまい、かつコメント無しでスミマセン。
最初に定義されてるmain()
が本体。ざっくり以下の様なことをやっています。
- コマンドラインでクライアントIDのキーファイルを(
.json
を抜いて)指定して実行します。 - キーファイルを読み込んでOAuth2のクライアントを作成。
- 初回の認証ではブラウザを開き、認可コードを受け取ります。
- 2回目以降の認証ではアクセストークンをリフレッシュしています。
- 何れにせよアクセストークンが得られたら、ファイルに保存し、
- 認可したアカウントのGoogle Driveのルートフォルダのファイル一覧を表示します。
※ 実際に動かすための情報は後述。
google-oauth2.js
"use strict"; const google = require("googleapis"); const OAuth2 = google.auth.OAuth2; const drive = google.drive({ version: 'v3' }); const listit = require("list-it"); const opn = require('opn'); const server = require("node-http-server"); const fs = require("fs"); var REDIRECT_PORT = 8800; var clientName = process.argv[2]; function main() { var clientFn = clientName + ".json"; readJsonFile(clientFn).then(function(client) { console.log(clientFn + " Loaded:"); console.log("Client:"); console.log(JSON.stringify(client, null, " ")); return createAuth(client); }).then(function(auth) { var acctokFn = clientName + "-auth.json"; return readJsonFile(acctokFn).then(function(acctok) { console.log(acctokFn + " loaded:"); console.log("Access Token:"); console.log(JSON.stringify(acctok, null, " ")); console.log("Refresh Tokens:"); return refreshAccessToken(auth, acctok).then(function(acctok) { console.log(JSON.stringify(acctok, null, " ")); return writeJsonFile(acctokFn, acctok); }).then(function() { return auth; }); }).catch(function(err) { console.log("No Access Token:"); var authUrl = auth.generateAuthUrl({ scope: "https://www.googleapis.com/auth/drive" }); console.log("Auth URL:" + authUrl); return getAuthCode(authUrl, REDIRECT_PORT).then(function(code) { console.log("Auth Code:" + code); return getAccessToken(auth, code); }).then(function(acctok) { console.log("Access Token:"); console.log(JSON.stringify(acctok, null, " ")); return writeJsonFile(acctokFn, acctok); }).then(function() { return auth; }); }); }).then(function(auth) { return getFileList({ auth: auth, q: "parents='root' and trashed=false" }); }).then(function(resp) { var list = listit.buffer({ "autoAlign" : true }); list.d([ "name", "mimeType" ]); resp.files.forEach(function(file) { list.d([ file.name, file.mimeType ]); }); console.log(list.toString()); }).then(function() { process.exit(0); }).catch(function(err) { console.log("Error: ", err.message); process.exit(-1); }); } function createAuth(client) { var credential; if("installed" in client) { credential = client.installed; } else if("web" in client) { credential = client.web; } var auth = new OAuth2( credential.client_id, credential.client_secret, "http://localhost:" + REDIRECT_PORT + "/"); return auth; } function refreshAccessToken(auth, oldTokens) { return new Promise(function(resolve, reject) { auth.credentials = oldTokens; auth.refreshAccessToken(function(err, refreshedTokens) { if (err) { reject(err); return; } auth.credentials = refreshedTokens; resolve(refreshedTokens); }); }); } function getAccessToken(auth, code) { return new Promise(function(resolve, reject) { auth.getToken(code, function(err, tokens) { if (err) { reject(err); return; } auth.credentials = tokens; resolve(tokens); }); }); } function getFileList(params) { return new Promise(function(resolve, reject) { drive.files.list(params, function(err, resp) { if(err) { reject(err); return; } resolve(resp); }); }); }; function getAuthCode(url, redirect_port) { opn(url); return waitHttpRequest(redirect_port, "/").then(function(http) { var code = http.request.uri.query.code; http.serve(http.request, http.response, ["Auth Code:", code].join(" ")); return code; }); } function waitHttpRequest(port, pathname) { return new Promise(function(resolve, reject) { var config = new server.Config(); config.port = port; server.onRequest = function(request, response, serve) { if(request.uri.pathname == pathname) { resolve({ request: request, response: response, serve: serve }); return true; } else { reject(new Error("Illegal access.")); return false; } }; server.deploy(config); }); } function readJsonFile(fn) { return new Promise(function(resolve, reject) { fs.readFile(fn, function(err, data) { if(err) { reject(err); return; } resolve(JSON.parse(data)); }); }); } function writeJsonFile(fn, obj) { return new Promise(function(resolve, reject) { fs.writeFile( fn, JSON.stringify(obj, null, " "), function(err) { if(err) { reject(err); return; } resolve(); }); }); } main();
依存モジュールのインストール
依存モジュールは以下4つ。
これらが利用可能な状態でなければ動きません。npmでインストールして下さい。
npm install googleapis list-it opn node-http-server
をコピペでいけます。
実行例
コマンドライン引数に、クライアントIDのキーファイルを指定して実行します。
以下実行例で、キーファイルは ./client_id.json
として保存されています。
ファイル一覧の桁がエラくズレていますが、実際のコンソールでは問題なしよ。 (デバッグ出力が長くて申し訳ない)
初回の認証実行例
$ node google-oauth2.js ./client_id ./client_id.json Loaded: Client: { "installed": { "client_id": "<クライアントID>", "project_id": "<プロジェクト名>", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://accounts.google.com/o/oauth2/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_secret": "<クライアントシークレット>", "redirect_uris": [ "urn:ietf:wg:oauth:2.0:oob", "http://localhost" ] } } No Access Token: Auth URL:<認証URL> Auth Code:<認可コード> Access Token: { "access_token": "<アクセストークン>", "refresh_token": "<リフレッシュトークン>", "token_type": "Bearer", "expiry_date": 1512041811335 } name mimeType ****** application/vnd.google-apps.spreadsheet ****** application/vnd.google-apps.folder ****** application/vnd.google-apps.script ****** application/vnd.google-apps.folder Document application/vnd.google-apps.folder Image application/vnd.google-apps.folder Google フォト application/vnd.google-apps.folder My Tracks application/vnd.google-apps.folder $
2回目以降のリフレッシュトークンを利用した実行例
$ node google-oauth2.js ./client_id ./client_id.json Loaded: Client: { "installed": { "client_id": "<クライアントID>", "project_id": "<プロジェクト名>", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://accounts.google.com/o/oauth2/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_secret": "<クライアントシークレット>", "redirect_uris": [ "urn:ietf:wg:oauth:2.0:oob", "http://localhost" ] } } ./client_id-auth.json loaded: Access Token: { "access_token": "<アクセストークン>", "token_type": "Bearer", "expiry_date": 1512021213468, "refresh_token": "<リフレッシュトークン>" } Refresh Tokens: { "access_token": "<アクセストークン>", "token_type": "Bearer", "expiry_date": 1512040667695, "refresh_token": "<リフレッシュトークン>" } name mimeType ****** application/vnd.google-apps.spreadsheet ****** application/vnd.google-apps.folder ****** application/vnd.google-apps.script ****** application/vnd.google-apps.folder Document application/vnd.google-apps.folder Image application/vnd.google-apps.folder Google フォト application/vnd.google-apps.folder My Tracks application/vnd.google-apps.folder $
まとめ
とりあえず認証が通ったなら、あとは各種APIを利用して、いろんなことができるので、おらワクワクすっぞ!
もう少しキチンとライブラリ的にまとめられたら独立したnpmとしてpublishするつもりですが、今の段階ではこの状態でごめんなさい。