feat: Dingtalk provider supports fetching organization email (#1636)

* feat(dingtalk): try to get email from corp app

* chore: format codes

* chore: format codes (#1)

* Delete .fleet directory

* fix: fix syntax errors

* Update dingtalk.go

* style: fmt codes with gofumpt

---------

Co-authored-by: aidenlu <aiden_lu@wochacha.com>
This commit is contained in:
aiden
2023-03-10 21:47:54 +08:00
committed by GitHub
parent 09e8408a3d
commit 2e3a323528

View File

@ -15,11 +15,9 @@
package idp package idp
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"log" "log"
"net/http" "net/http"
"strings" "strings"
@ -170,10 +168,18 @@ func (idp *DingTalkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
Email: dtUserInfo.Email, Email: dtUserInfo.Email,
AvatarUrl: dtUserInfo.AvatarUrl, AvatarUrl: dtUserInfo.AvatarUrl,
} }
isUserInOrg, err := idp.isUserInOrg(userInfo.UnionId)
if !isUserInOrg { corpAccessToken := idp.getInnerAppAccessToken()
userId, err := idp.getUserId(userInfo.UnionId, corpAccessToken)
if err != nil {
return nil, err return nil, err
} }
corpEmail, err := idp.getUserCorpEmail(userId, corpAccessToken)
if err == nil && corpEmail != "" {
userInfo.Email = corpEmail
}
return &userInfo, nil return &userInfo, nil
} }
@ -202,23 +208,14 @@ func (idp *DingTalkIdProvider) postWithBody(body interface{}, url string) ([]byt
} }
func (idp *DingTalkIdProvider) getInnerAppAccessToken() string { func (idp *DingTalkIdProvider) getInnerAppAccessToken() string {
appKey := idp.Config.ClientID
appSecret := idp.Config.ClientSecret
body := make(map[string]string) body := make(map[string]string)
body["appKey"] = appKey body["appKey"] = idp.Config.ClientID
body["appSecret"] = appSecret body["appSecret"] = idp.Config.ClientSecret
bodyData, err := json.Marshal(body) respBytes, err := idp.postWithBody(body, "https://api.dingtalk.com/v1.0/oauth2/accessToken")
if err != nil {
log.Println(err.Error())
}
reader := bytes.NewReader(bodyData)
request, err := http.NewRequest("POST", "https://api.dingtalk.com/v1.0/oauth2/accessToken", reader)
request.Header.Set("Content-Type", "application/json;charset=UTF-8")
resp, err := idp.Client.Do(request)
respBytes, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
log.Println(err.Error()) log.Println(err.Error())
} }
var data struct { var data struct {
ExpireIn int `json:"expireIn"` ExpireIn int `json:"expireIn"`
AccessToken string `json:"accessToken"` AccessToken string `json:"accessToken"`
@ -230,34 +227,53 @@ func (idp *DingTalkIdProvider) getInnerAppAccessToken() string {
return data.AccessToken return data.AccessToken
} }
func (idp *DingTalkIdProvider) isUserInOrg(unionId string) (bool, error) { func (idp *DingTalkIdProvider) getUserId(unionId string, accessToken string) (string, error) {
body := make(map[string]string) body := make(map[string]string)
body["unionid"] = unionId body["unionid"] = unionId
bodyData, err := json.Marshal(body) respBytes, err := idp.postWithBody(body, "https://oapi.dingtalk.com/topapi/user/getbyunionid?access_token="+accessToken)
if err != nil { if err != nil {
log.Println(err.Error()) return "", err
}
reader := bytes.NewReader(bodyData)
accessToken := idp.getInnerAppAccessToken()
request, _ := http.NewRequest("POST", "https://oapi.dingtalk.com/topapi/user/getbyunionid?access_token="+accessToken, reader)
request.Header.Set("Content-Type", "application/json;charset=UTF-8")
resp, err := idp.Client.Do(request)
respBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println(err.Error())
} }
var data struct { var data struct {
ErrCode int `json:"errcode"` ErrCode int `json:"errcode"`
ErrMessage string `json:"errmsg"` ErrMessage string `json:"errmsg"`
Result struct {
UserId string `json:"userid"`
} `json:"result"`
} }
err = json.Unmarshal(respBytes, &data) err = json.Unmarshal(respBytes, &data)
if err != nil { if err != nil {
log.Println(err.Error()) return "", err
} }
if data.ErrCode == 60121 { if data.ErrCode == 60121 {
return false, fmt.Errorf("the user is not found in the organization where clientId and clientSecret belong") return "", fmt.Errorf("the user is not found in the organization where clientId and clientSecret belong")
} else if data.ErrCode != 0 { } else if data.ErrCode != 0 {
return false, fmt.Errorf(data.ErrMessage) return "", fmt.Errorf(data.ErrMessage)
} }
return true, nil return data.Result.UserId, nil
}
func (idp *DingTalkIdProvider) getUserCorpEmail(userId string, accessToken string) (string, error) {
body := make(map[string]string)
body["userid"] = userId
respBytes, err := idp.postWithBody(body, "https://oapi.dingtalk.com/topapi/v2/user/get?access_token="+accessToken)
if err != nil {
return "", err
}
var data struct {
ErrMessage string `json:"errmsg"`
Result struct {
Email string `json:"email"`
} `json:"result"`
}
err = json.Unmarshal(respBytes, &data)
if err != nil {
return "", err
}
if data.ErrMessage != "ok" {
return "", fmt.Errorf(data.ErrMessage)
}
return data.Result.Email, nil
} }