SalesforceのRest API を呼ぶ

REST API

SalesforceはOAuth2の認証を使ってAPIを呼ぶことができる。
今回はそれを試したいと思う。

参考にしたのはこれ。
Force.com REST API 開発者ガイド

準備

Salesforce 開発者 からサインアップすると、無料の開発エディションが使用できる。
ログインしたら、設定からアプリケーションを選択し、最下部にある接続アプリケーションの新規ボタンをクリック。

接続アプリケーションの作成

接続アプリケーション名などは適当に入力。
OAuth設定の有効化にチェック。コールバックURLは環境に合わせて設定。今回はhttp://localhost:8080/とした。
選択したOAuth範囲は、「フルアクセス」と「ユーザに代わっていつでも要求を実行」の2つ。後者がないとRefresh Tokenを取得できない。

scope

これで保存。
保存後、コンシューマ鍵とコンシューマの秘密の2つをコピーしておく。

Go言語で実装

Go言語で実装していく。
まず、認証のEndpointだが、上記pdfによると、以下の2つのようだ。
ただし、sandbox環境の場合は、loginの箇所をtestにする。

そして、APIを呼ぶ際のEndpointは以下である。
xxxの箇所には各自ログインしたときのSalesforceのアドレスと同じものが入る。例えばap4など。

手順としては認証用のURLを作り、コールバック用に指定したlocalhost:8080でサーバを起動しておく。
URLにアクセスすると、ユーザに確認が求められ、承認するとリダイレクトされる。
リダイレクト時にcodeが付与されているので、これを使用し、トークンを手に入れられる。
トークンを手に入れたら、APIを呼ぶことができるが、今回はAccountに新しいレコードを追加してみる。
以上の手順のソースが以下である。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net/http"
"golang.org/x/oauth2"
)
var conf *oauth2.Config
const (
ClientId = "xxxxxxxxxxx" //コンシューマ鍵
ClientSecret = "xxxxxxxxxxx" //コンシューマの秘密
AuthURL = "https://login.salesforce.com/services/oauth2/authorize"
TokenURL = "https://login.salesforce.com/services/oauth2/token"
RedirectURL = "http://localhost:8080/"
ApiURL = "https://xxx.salesforce.com/services/data/v38.0/"
)
func main() {
conf = &oauth2.Config{
ClientID: ClientId,
ClientSecret: ClientSecret,
Endpoint: oauth2.Endpoint{
AuthURL: AuthURL,
TokenURL: TokenURL,
},
RedirectURL: RedirectURL,
}
url := conf.AuthCodeURL("")
fmt.Println(url)
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
func handler(w http.ResponseWriter, r *http.Request) {
code := r.FormValue("code")
fmt.Println(code)
token, err := conf.Exchange(oauth2.NoContext, code)
if err != nil {
log.Fatal("exchange error", err.Error())
return
}
client := conf.Client(oauth2.NoContext, token)
jsonStr := `{"Name" : "John"}`
req, err := http.NewRequest("POST", ApiURL+"sobjects/Account", bytes.NewBuffer([]byte(jsonStr)))
if err != nil {
log.Fatal(err.Error())
}
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
log.Fatal(err.Error())
}
body, _ := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
println(string(body))
}

上記実行し、Account(取引先)に名前がJohnの新しいレコードが作られるのを確認できた。

SalesforceのOAuth2でリフレッシュトークンが取得できない

最近、仕事でSalesforceを触っている。
REST APIを使っていろいろしようとしたのだが、OAuth2のトークンを交換するところで、なぜかRefresh Tokenが空で取得できなくてはまった。
Access Tokenは取得できるのに、Refresh Tokenが空。

答えはここで判明。
oauth authorization no longer returning refresh token (HELP!)

Salesforce側の接続アプリケーションの設定でスコープを定義するところがあるのだが、
そこに「ユーザに代わっていつでも要求を実行(refresh_token,offline_access)」というスコープがあり、これが必要なようだ。
フルアクセスのスコープを入れてたのでこれで全て入っているとばかり思っていたが、そうではないらしい。

scope

これで無事Refresh Tokenが取れた。

更新時にFTP情報の入力を促されるのを改善

WordPress更新時にFTP情報の入力が必要と表示される

WordPressの更新通知が来ていたので更新しようとしたところ、以下のような文言の画面が表示されてしまった。
「要求されたアクションを実行するには、WordPress が Web サーバーにアクセスする必要があります。 次に進むには FTP の接続情報を入力してください。 接続情報が思い出せない場合は、ホスティング担当者に問い合わせてください。」
どうやら、パーミッションや所有者の問題で更新できないようである。

所有者の見直し

サーバはnginxなので、phpの実行時ユーザはnginxになる。そのため、WordPressディレクトリの所有者をnginxに。
パーミッションも問題なし。これで大丈夫のはずだが、なぜか直らず。

問題はSELinuxだった

正直、1時間以上はまった。どうみても所有者もパーミッションも大丈夫なのに更新できない。
ふと、phpでmkdirとかできるのだろうかと思い、実行したところ Permission Denied。なんかおかしい。
そして、やっと原因がわかった。問題はSELinuxだったのだ。以下の記事が答え。
PHPでPermission deniedが出てしまう(CentOS7)

chcon -R -t httpd_sys_content_rw_t wp
WordPressのフォルダに対して、以上のように実行して、解決した。

Node.jsでAES暗号化・復号化

Node.jsでAES暗号化・復号化する話。
今回は、AES256bitで行う。
cryptoモジュール使えば簡単にできるのだが、createCipherメソッドだと鍵と初期化ベクトルを指定できない。
代わりにcreateCipherivを使うことでそれらを指定できる。
以下コード。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var cryp = require("crypto");
var key = new Buffer('bFr3r5iRkiTWf3r-a8GsHVZUgpAtDL7X', 'utf8'); //鍵256bit
var iv = new Buffer('5xWAzpRh6TgybfGd', 'utf8');//初期化ベクトル128bit
var text = "あいうえお";
//暗号化
var cipher = cryp.createCipheriv('aes-256-cbc', key, iv);
var encoded = cipher.update(text, 'utf8', 'binary');
encoded += cipher.final('binary');
//復号化
var decipher = cryp.createDecipheriv('aes256', key, iv);
var decoded = decipher.update(encoded, 'binary', 'utf8');
decoded += decipher.final('utf8');
console.log(decoded); //あいうえお

go言語でリバースプロキシ

httputilのReverseProxyを使う

go言語でリバースプロキシを立てるには、httputilパッケージにあるReverseProxyを使うと簡単。
ReverseProxyはhttp.Handlerインタフェースを実装していて、http.ServerのHandlerに渡せる。
以下はlocalhost:9000のリクエストをlocalhost:9001に移譲する処理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main
import (
"log"
"net/http"
"net/http/httputil"
)
func main() {
director := func(request *http.Request) {
request.URL.Scheme = "http"
request.URL.Host = ":9001"
}
rp := &httputil.ReverseProxy{Director: director}
server := http.Server{
Addr: ":9000",
Handler: rp,
}
if err := server.ListenAndServe(); err != nil {
log.Fatal(err.Error())
}
}

別ドメインへの移譲

あんまりないケースかもしれないが、localhost:9000のアクセスを別のドメインに移譲する場合は以下。
bodyやヘッダをそのままコピーして新しいhttpのリクエストを生成しなおす必要がある。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package main
import (
"bytes"
"io/ioutil"
"log"
"net/http"
"net/http/httputil"
)
func main() {
director := func(request *http.Request) {
url := *request.URL
url.Scheme = "https"
url.Host = "kido0617.github.io"
buffer, err := ioutil.ReadAll(request.Body)
if err != nil {
log.Fatal(err.Error())
}
req, err := http.NewRequest(request.Method, url.String(), bytes.NewBuffer(buffer))
if err != nil {
log.Fatal(err.Error())
}
req.Header = request.Header
*request = *req
}
rp := &httputil.ReverseProxy{Director: director}
server := http.Server{
Addr: ":9000",
Handler: rp,
}
if err := server.ListenAndServe(); err != nil {
log.Fatal(err.Error())
}
}

参考

Golang Reverse Proxy
Reverse Proxy in Go
ichigo