azure.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. package azure
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "github.com/gravitl/netmaker/logic"
  7. "github.com/gravitl/netmaker/pro/idp"
  8. "net/http"
  9. "net/url"
  10. )
  11. type Client struct {
  12. clientID string
  13. clientSecret string
  14. tenantID string
  15. }
  16. func NewAzureEntraIDClient() *Client {
  17. settings := logic.GetServerSettings()
  18. return &Client{
  19. clientID: settings.ClientID,
  20. clientSecret: settings.ClientSecret,
  21. tenantID: settings.AzureTenant,
  22. }
  23. }
  24. func (a *Client) GetUsers() ([]idp.User, error) {
  25. accessToken, err := a.getAccessToken()
  26. if err != nil {
  27. return nil, err
  28. }
  29. client := &http.Client{}
  30. req, err := http.NewRequest("GET", "https://graph.microsoft.com/v1.0/users?$select=id,userPrincipalName,displayName,accountEnabled", nil)
  31. if err != nil {
  32. return nil, err
  33. }
  34. req.Header.Add("Authorization", "Bearer "+accessToken)
  35. req.Header.Add("Accept", "application/json")
  36. resp, err := client.Do(req)
  37. if err != nil {
  38. return nil, err
  39. }
  40. defer func() {
  41. _ = resp.Body.Close()
  42. }()
  43. var users getUsersResponse
  44. err = json.NewDecoder(resp.Body).Decode(&users)
  45. if err != nil {
  46. return nil, err
  47. }
  48. retval := make([]idp.User, len(users.Value))
  49. for i, user := range users.Value {
  50. retval[i] = idp.User{
  51. ID: user.Id,
  52. Username: user.UserPrincipalName,
  53. DisplayName: user.DisplayName,
  54. AccountDisabled: !user.AccountEnabled,
  55. }
  56. }
  57. return retval, nil
  58. }
  59. func (a *Client) GetGroups() ([]idp.Group, error) {
  60. accessToken, err := a.getAccessToken()
  61. if err != nil {
  62. return nil, err
  63. }
  64. client := &http.Client{}
  65. req, err := http.NewRequest("GET", "https://graph.microsoft.com/v1.0/groups?$select=id,displayName&$expand=members($select=id)", nil)
  66. if err != nil {
  67. return nil, err
  68. }
  69. req.Header.Add("Authorization", "Bearer "+accessToken)
  70. req.Header.Add("Accept", "application/json")
  71. resp, err := client.Do(req)
  72. if err != nil {
  73. return nil, err
  74. }
  75. defer func() {
  76. _ = resp.Body.Close()
  77. }()
  78. var groups getGroupsResponse
  79. err = json.NewDecoder(resp.Body).Decode(&groups)
  80. if err != nil {
  81. return nil, err
  82. }
  83. retval := make([]idp.Group, len(groups.Value))
  84. for i, group := range groups.Value {
  85. retvalMembers := make([]string, len(group.Members))
  86. for j, member := range group.Members {
  87. retvalMembers[j] = member.Id
  88. }
  89. retval[i] = idp.Group{
  90. ID: group.Id,
  91. Name: group.DisplayName,
  92. Members: retvalMembers,
  93. }
  94. }
  95. return retval, nil
  96. }
  97. func (a *Client) getAccessToken() (string, error) {
  98. tokenURL := fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", a.tenantID)
  99. var data = url.Values{}
  100. data.Set("grant_type", "client_credentials")
  101. data.Set("client_id", a.clientID)
  102. data.Set("client_secret", a.clientSecret)
  103. data.Set("scope", "https://graph.microsoft.com/.default")
  104. resp, err := http.PostForm(tokenURL, data)
  105. if err != nil {
  106. return "", err
  107. }
  108. defer func() {
  109. _ = resp.Body.Close()
  110. }()
  111. var tokenResp map[string]interface{}
  112. err = json.NewDecoder(resp.Body).Decode(&tokenResp)
  113. if err != nil {
  114. return "", err
  115. }
  116. if token, ok := tokenResp["access_token"].(string); ok {
  117. return token, nil
  118. }
  119. return "", errors.New("failed to get access token")
  120. }
  121. type getUsersResponse struct {
  122. OdataContext string `json:"@odata.context"`
  123. Value []struct {
  124. Id string `json:"id"`
  125. UserPrincipalName string `json:"userPrincipalName"`
  126. DisplayName string `json:"displayName"`
  127. AccountEnabled bool `json:"accountEnabled"`
  128. } `json:"value"`
  129. }
  130. type getGroupsResponse struct {
  131. OdataContext string `json:"@odata.context"`
  132. Value []struct {
  133. Id string `json:"id"`
  134. DisplayName string `json:"displayName"`
  135. Members []struct {
  136. OdataType string `json:"@odata.type"`
  137. Id string `json:"id"`
  138. } `json:"members"`
  139. } `json:"value"`
  140. }