fossteams-api/pkg/authz.go
Denys Vitali 55aeb90907
misc improvements
- Refactor
- Fix https://github.com/fossteams/teams-api/issues/1
- Address concerns of https://github.com/fossteams/teams-cli/issues/1
- Generalize HTTP error messages
- Add JSON for tests
- Return api.GuestUserNotRedeemed error if tenant is missing on Authz
refresh
- Create models package
- Use Me() for `TestGetUserProfilePicture` test
2021-04-13 00:32:44 +02:00

175 lines
5.9 KiB
Go

package api
import (
"encoding/json"
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/fossteams/teams-api/pkg/errors"
"io"
"net/http"
"reflect"
)
type Tokens struct {
SkypeToken string `json:"skypeToken"`
ExpiresIn int `json:"expiresIn"`
}
type Region string
type Partition string
const (
Emea Region = "emea"
)
const (
Emea1 Partition = "emea01"
)
type RegionGTMs struct {
Ams string `json:"ams"`
AmsV2 string `json:"amsV2"`
AmdS2S string `json:"amdS2S"`
AmsS2S string `json:"amsS2S"`
AppsDataLayerService string `json:"appsDataLayerService"`
AppsDataLayerServiceS2S string `json:"appsDataLayerServiceS2S"`
CallingCallControllerServiceURL string `json:"calling_callControllerServiceUrl"`
CallingCallStoreUrl string `json:"calling_callStoreUrl"`
CallingConversationServiceURL string `json:"calling_conversationServiceUrl"`
CallingKeyDistributionUrl string `json:"calling_keyDistributionUrl"`
CallingPotentialCallRequestUrl string `json:"calling_potentialCallRequestUrl"`
CallingSharedLineOptionsUrl string `json:"calling_sharedLineOptionsUrl"`
CallingUdpTransportUrl string `json:"calling_udpTransportUrl"`
CallingUploadLogRequestUrl string `json:"calling_uploadLogRequestUrl"`
CallingS2SBroker string `json:"callingS2S_Broker"`
CallingS2SCallController string `json:"callingS2S_CallController"`
CallingS2SCallStore string `json:"callingS2S_CallStore"`
CallingS2SContentSharing string `json:"callingS2S_ContentSharing"`
CallingS2SConversationService string `json:"callingS2S_ConversationService"`
CallingS2SEnterpriseProxy string `json:"callingS2S_EnterpriseProxy"`
CallingS2SMediaController string `json:"callingS2S_MediaController"`
CallingS2SPlatformMediaAgent string `json:"callingS2S_PlatformMediaAgent"`
ChatService string `json:"chatService"`
ChatServiceAggregator string `json:"chatServiceAggregator"`
ChatServiceS2S string `json:"chatServiceS2S"`
Drad string `json:"drad"`
MailHookS2S string `json:"mailhookS2S"`
MiddleTier string `json:"middleTier"`
MiddleTierS2S string `json:"middleTierS2S"`
MtImageService string `json:"mtImageService"`
PowerPointStateService string `json:"powerPointStateService"`
Search string `json:"search"`
SearchTelemetry string `json:"searchTelemetry"`
TeamsAndChannelsService string `json:"teamsAndChannelsService"`
TeamsAndChannelsProvisioningService string `json:"teamsAndChannelsProvisioningService"`
Urlp string `json:"urlp"`
UrlpV2 string `json:"urlpV2"`
UnifiedPresence string `json:"unifiedPresence"`
UserEntitlementService string `json:"userEntitlementService"`
UserIntelligenceService string `json:"userIntelligenceService"`
UserProfileService string `json:"userProfileService"`
UserProfileServiceS2S string `json:"userProfileServiceS2S"`
}
type RegionSettings struct {
IsUnifiedPresenceEnabled bool
IsOutOfOfficeIntegrationEnabled bool
IsContactMigrationEnabled bool
IsAppsDiscoveryEnabled bool
IsFederationEnabled bool
}
type LicenseDetails struct {
IsFreemium bool
IsBasicLiveEventsEnabled bool
IsTrial bool
IsAdvComms bool
}
type AuthzResponse struct {
Tokens Tokens `json:"tokens"`
Region Region `json:"region"`
Partition Partition `json:"partition"`
RegionGtms RegionGTMs `json:"regionGtms"`
RegionSettings RegionSettings `json:"regionSettings"`
LicenseDetails LicenseDetails
}
type AuthClient struct {
client *http.Client
}
func New(client *http.Client) AuthClient {
if client == nil {
client = http.DefaultClient
}
return AuthClient{client: client}
}
type AuthzType = string
const (
AuthzRefresh AuthzType = "TokenRefresh"
)
func (a AuthClient) Authz(token *RootSkypeToken, authzType AuthzType) (*SkypeToken, error) {
req, err := http.NewRequest("POST", TEAMS_API_ENDPOINT+"/authsvc/v1.0/authz", nil)
if err != nil {
return nil, fmt.Errorf("unable to create Authz request: %v", err)
}
req.Header.Add("ms-teams-authz-type", authzType)
req.Header.Add("Authorization", "Bearer "+token.Inner.Raw)
resp, err := a.client.Do(req)
if err != nil {
return nil, fmt.Errorf("unable to perform authz request: %v", err)
}
expectedResponseCode := http.StatusOK
if resp.StatusCode != expectedResponseCode {
bodyBytes, readErr := io.ReadAll(resp.Body)
if readErr != nil {
return nil, errors.NewHTTPError(expectedResponseCode, resp.StatusCode, nil)
}
// Parse response
var errorResponse AuthzError
err = json.Unmarshal(bodyBytes, &errorResponse)
if err != nil {
return nil, errors.NewHTTPError(expectedResponseCode, resp.StatusCode, bodyBytes)
}
return nil, errorResponse
}
dec := json.NewDecoder(resp.Body)
var authzResp AuthzResponse
err = dec.Decode(&authzResp)
if err != nil {
return nil, fmt.Errorf("unable to decode authz response JSON: %v", err)
}
skypeJwt, err := jwt.Parse(authzResp.Tokens.SkypeToken, nil)
if err != nil {
shouldThrow := true
switch err.(type) {
case *jwt.ValidationError:
if err.(*jwt.ValidationError).Errors == jwt.ValidationErrorUnverifiable {
shouldThrow = false
}
default:
fmt.Printf("type=%v", reflect.TypeOf(err))
}
if shouldThrow {
return nil, fmt.Errorf("unable to decode Skype JWT: %v", err)
}
}
skypeToken := SkypeToken{
Inner: skypeJwt,
Type: TokenBearer,
}
return &skypeToken, nil
}