feat(channels): align twitter authorization
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTwitterAuthorizationRouter() *gin.Engine {
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
r.RedirectTrailingSlash = false
|
||||
handler := NewTwitterChannelHandler(nil, nil, nil, nil)
|
||||
r.POST("/api/v1/accounts/:account_id/twitter/authorization", handler.ChatwootAuthorization)
|
||||
return r
|
||||
}
|
||||
|
||||
func TestTwitterAuthorization_BadAccountID(t *testing.T) {
|
||||
r := setupTwitterAuthorizationRouter()
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest(http.MethodPost, "/api/v1/accounts/abc/twitter/authorization", nil)
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
var resp map[string]interface{}
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
errBody := resp["error"].(map[string]interface{})
|
||||
assert.Contains(t, errBody["message"], "invalid account_id")
|
||||
}
|
||||
|
||||
func TestTwitterAuthorization_ReturnsChatwootPayload(t *testing.T) {
|
||||
t.Setenv("TWITTER_CONSUMER_KEY", "twitter-key")
|
||||
t.Setenv("TWITTER_CONSUMER_SECRET", "twitter-secret")
|
||||
t.Setenv("TWITTER_OAUTH_REQUEST_TOKEN_URL", "https://oauth.example.test/request_token")
|
||||
t.Setenv("TWITTER_API_BASE_URL", "https://api.twitter.test")
|
||||
t.Setenv("FRONTEND_URL", "https://app.example.test/")
|
||||
|
||||
originalClient := twitterAuthorizationHTTPClient
|
||||
twitterAuthorizationHTTPClient = &http.Client{Transport: twitterAuthorizationRoundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||
assert.Equal(t, http.MethodPost, req.Method)
|
||||
assert.Equal(t, "https://oauth.example.test/request_token", req.URL.String())
|
||||
authHeader := req.Header.Get("Authorization")
|
||||
assert.Contains(t, authHeader, "OAuth ")
|
||||
assert.Contains(t, authHeader, `oauth_consumer_key="twitter-key"`)
|
||||
assert.Contains(t, authHeader, "oauth_callback=")
|
||||
return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(strings.NewReader("oauth_token=request-token&oauth_token_secret=request-secret")), Header: http.Header{}}, nil
|
||||
})}
|
||||
t.Cleanup(func() { twitterAuthorizationHTTPClient = originalClient })
|
||||
|
||||
r := setupTwitterAuthorizationRouter()
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest(http.MethodPost, "/api/v1/accounts/42/twitter/authorization", nil)
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
assert.Equal(t, true, resp["success"])
|
||||
assert.Equal(t, "https://api.twitter.test/oauth/authorize?oauth_token=request-token", resp["url"])
|
||||
}
|
||||
|
||||
func TestTwitterAuthorization_SignedStateMatchesCallback(t *testing.T) {
|
||||
t.Setenv("TWITTER_CONSUMER_KEY", "twitter-key")
|
||||
t.Setenv("TWITTER_CONSUMER_SECRET", "twitter-secret")
|
||||
t.Setenv("TWITTER_OAUTH_REQUEST_TOKEN_URL", "https://oauth.example.test/request_token")
|
||||
t.Setenv("FRONTEND_URL", "https://app.example.test")
|
||||
|
||||
originalClient := twitterAuthorizationHTTPClient
|
||||
var callbackURL string
|
||||
twitterAuthorizationHTTPClient = &http.Client{Transport: twitterAuthorizationRoundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||
authHeader := req.Header.Get("Authorization")
|
||||
for _, part := range strings.Split(strings.TrimPrefix(authHeader, "OAuth "), ",") {
|
||||
part = strings.TrimSpace(part)
|
||||
if strings.HasPrefix(part, "oauth_callback=") {
|
||||
callbackURL = strings.Trim(strings.TrimPrefix(part, "oauth_callback="), `"`)
|
||||
decoded, err := url.QueryUnescape(callbackURL)
|
||||
require.NoError(t, err)
|
||||
callbackURL = decoded
|
||||
}
|
||||
}
|
||||
return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(strings.NewReader("oauth_token=request-token")), Header: http.Header{}}, nil
|
||||
})}
|
||||
t.Cleanup(func() { twitterAuthorizationHTTPClient = originalClient })
|
||||
|
||||
r := setupTwitterAuthorizationRouter()
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest(http.MethodPost, "/api/v1/accounts/42/twitter/authorization", nil)
|
||||
r.ServeHTTP(w, req)
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
parsed, err := url.Parse(callbackURL)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "https://app.example.test/twitter/callback", parsed.Scheme+"://"+parsed.Host+parsed.Path)
|
||||
claims := jwt.MapClaims{}
|
||||
token, err := jwt.ParseWithClaims(parsed.Query().Get("state"), claims, func(token *jwt.Token) (any, error) {
|
||||
return []byte("twitter-secret"), nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.True(t, token.Valid)
|
||||
assert.Equal(t, float64(42), claims["sub"])
|
||||
}
|
||||
|
||||
func TestTwitterAuthorization_NotConfigured(t *testing.T) {
|
||||
t.Setenv("TWITTER_CONSUMER_KEY", "")
|
||||
t.Setenv("TWITTER_CONSUMER_SECRET", "")
|
||||
r := setupTwitterAuthorizationRouter()
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest(http.MethodPost, "/api/v1/accounts/42/twitter/authorization", nil)
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusUnprocessableEntity, w.Code)
|
||||
var resp map[string]interface{}
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
assert.Equal(t, false, resp["success"])
|
||||
}
|
||||
|
||||
type twitterAuthorizationRoundTripFunc func(*http.Request) (*http.Response, error)
|
||||
|
||||
func (f twitterAuthorizationRoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return f(req)
|
||||
}
|
||||
Reference in New Issue
Block a user