This repository has been archived by the owner on Jul 19, 2023. It is now read-only.
forked from Vonage/vonage-go-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
240 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
package nexmo | ||
|
||
import ( | ||
"crypto/rsa" | ||
"math/rand" | ||
"strconv" | ||
"time" | ||
|
||
"fmt" | ||
|
||
"github.com/dghubble/sling" | ||
"github.com/dgrijalva/jwt-go" | ||
) | ||
|
||
type AuthType uint8 | ||
|
||
type RandomProvider interface { | ||
Int31() int32 | ||
} | ||
|
||
const ( | ||
ApiSecretAuth AuthType = iota + 1 | ||
ApiSecretPathAuth | ||
JwtAuth | ||
) | ||
|
||
type AuthSet struct { | ||
apiSecret *apiSecretAuth | ||
appAuth *applicationAuth | ||
} | ||
|
||
func NewAuthSet() *AuthSet { | ||
return new(AuthSet) | ||
} | ||
|
||
func (a *AuthSet) SetAPISecret(apiKey, apiSecret string) { | ||
a.apiSecret = &apiSecretAuth{ | ||
apiKey: apiKey, | ||
apiSecret: apiSecret, | ||
} | ||
} | ||
|
||
func (a *AuthSet) GenerateToken() (string, error) { | ||
if a.appAuth == nil { | ||
return "", fmt.Errorf("must call SetApplicationAuth before calling GenerateToken") | ||
} | ||
return a.appAuth.generateToken() | ||
} | ||
|
||
func (a *AuthSet) SetApplicationAuth(appID string, key []byte) error { | ||
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(key) | ||
if err != nil { | ||
return err | ||
} | ||
a.appAuth = &applicationAuth{ | ||
appID: appID, | ||
privateKey: privateKey, | ||
r: rand.New(rand.NewSource(time.Now().UnixNano())), | ||
} | ||
return nil | ||
} | ||
|
||
func (a *AuthSet) ApplyAuth(acceptableAuths []AuthType, sling *sling.Sling, request apiSecretRequest) error { | ||
for _, acceptableAuth := range acceptableAuths { | ||
switch acceptableAuth { | ||
case ApiSecretAuth: | ||
if a.apiSecret != nil { | ||
return a.apiSecret.apply(request) | ||
} | ||
case ApiSecretPathAuth: | ||
if a.apiSecret != nil { | ||
sling.Path(fmt.Sprintf("%s/%s", a.apiSecret.apiKey, a.apiSecret.apiSecret)) | ||
} | ||
case JwtAuth: | ||
token, err := a.appAuth.generateToken() | ||
if err != nil { | ||
return err | ||
} | ||
if a.appAuth != nil { | ||
sling.Set("Authorization", fmt.Sprintf("Bearer %v", token)) | ||
} | ||
} | ||
} | ||
return fmt.Errorf("AuthSet not configured with credentials for %x", acceptableAuths) | ||
} | ||
|
||
type apiSecretRequest interface { | ||
applyAPISecret(apiKey, apiSecret string) | ||
} | ||
|
||
type apiSecretAuth struct { | ||
apiKey string | ||
apiSecret string | ||
} | ||
|
||
func (a apiSecretAuth) apply(request apiSecretRequest) error { | ||
if request == nil { | ||
return fmt.Errorf("cannot apply api_key and api_secret to a nil request") | ||
} | ||
request.applyAPISecret(a.apiKey, a.apiSecret) | ||
return nil | ||
} | ||
|
||
type applicationAuth struct { | ||
appID string | ||
privateKey *rsa.PrivateKey | ||
r RandomProvider | ||
} | ||
|
||
type jwtClaims struct { | ||
ApplicationID string `json:"application_id"` | ||
jwt.StandardClaims | ||
} | ||
|
||
func (a applicationAuth) generateToken() (string, error) { | ||
claims := jwtClaims{ | ||
a.appID, | ||
jwt.StandardClaims{ | ||
Id: strconv.Itoa(int(a.r.Int31())), | ||
IssuedAt: time.Now().UTC().Unix(), | ||
}, | ||
} | ||
token := jwt.NewWithClaims(jwt.GetSigningMethod("RS256"), claims) | ||
return token.SignedString(a.privateKey) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package nexmo | ||
|
||
import ( | ||
"net/http" | ||
|
||
"fmt" | ||
|
||
"github.com/dghubble/sling" | ||
) | ||
|
||
type InsightService struct { | ||
sling *sling.Sling | ||
authSet *AuthSet | ||
} | ||
|
||
type BasicInsightRequest struct { | ||
Number string | ||
Country string | ||
Cnam *bool | ||
} | ||
|
||
type basicInsightJSONRequest struct { | ||
APIKey string `json:"api_key"` | ||
APISecret string `json:"api_secret"` | ||
Number string `json:"number,omitempty"` | ||
Country string `json:"country,omitempty"` | ||
} | ||
|
||
type BasicInsightResponse struct { | ||
Status int64 `json:"status"` | ||
StatusMessage string `json:"status_message,omitempty"` | ||
ErrorText string `json:"error_text,omitempty"` | ||
InternationalFormatNumber string `json:"international_format_number,omitempty"` | ||
NationalFormatNumber string `json:"national_format_number,omitempty"` | ||
CountryCode string `json:"country_code,omitempty"` | ||
CountryCodeIso3 string `json:"country_code_iso3,omitempty"` | ||
CountryName string `json:"country_name,omitempty"` | ||
CountryPrefix string `json:"country_prefix,omitempty"` | ||
} | ||
|
||
func (r *BasicInsightResponse) responseError() error { | ||
if r.Status != 0 { | ||
if r.StatusMessage != "" { | ||
return fmt.Errorf("%d: %s", r.Status, r.StatusMessage) | ||
} | ||
return fmt.Errorf("%d: %s", r.Status, r.ErrorText) | ||
} | ||
return nil | ||
} | ||
|
||
func newInsightService(base *sling.Sling, authSet *AuthSet) *InsightService { | ||
sling := base.Base("https://api.nexmo.com/ni/") | ||
return &InsightService{ | ||
sling: sling, | ||
authSet: authSet, | ||
} | ||
} | ||
|
||
func (c *InsightService) GetBasicInsight(request BasicInsightRequest) (BasicInsightResponse, *http.Response, error) { | ||
if c.authSet.apiSecret == nil { | ||
return BasicInsightResponse{}, nil, fmt.Errorf("Cannot call GetBasicInsight without providing APISecretAuth") | ||
} | ||
jsonRequest := basicInsightJSONRequest{ | ||
APIKey: c.authSet.apiSecret.apiKey, | ||
APISecret: c.authSet.apiSecret.apiSecret, | ||
Number: request.Number, | ||
Country: request.Country, | ||
} | ||
|
||
insightResponse := new(BasicInsightResponse) | ||
resp, err := c.sling.New().Post("basic/json").BodyJSON(jsonRequest).ReceiveSuccess(insightResponse) | ||
if err != nil { | ||
return *insightResponse, resp, err | ||
} | ||
err = insightResponse.responseError() | ||
return *insightResponse, resp, err | ||
} | ||
|
||
func (c *InsightService) SetBaseURL(baseURL string) { | ||
c.sling.Base(baseURL) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package nexmo | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
|
||
"github.com/dghubble/sling" | ||
) | ||
|
||
type Client struct { | ||
sling *sling.Sling | ||
Insight *InsightService | ||
//SMS *SMSService | ||
} | ||
|
||
func NewClient(httpClient *http.Client, authSet *AuthSet) *Client { | ||
base := sling.New(). | ||
Client(httpClient). | ||
Set("User-Agent", "nexmo-go/2.0 (judy2k)") | ||
return &Client{ | ||
sling: base, | ||
Insight: newInsightService(base.New(), authSet), | ||
//SMS: newSMSService(base.New(), authSet), | ||
} | ||
} | ||
|
||
type APIError struct { | ||
Status int64 | ||
ErrorMessage string | ||
} | ||
|
||
func (a APIError) Error() string { | ||
return fmt.Sprintf("%d: %s", a.Status, a.ErrorMessage) | ||
} |