feat: a Fields pane for anything but a secret, first for Azure accounts: fields and groups to move through, filter and copy (#7)
This commit was merged in pull request #7.
This commit is contained in:
1 parent
f6c4f5f6f6
commit
71396f1e02
13 files changed
+404
-37
No files matched your search
@@ -1,10 +1,15 @@
|
||||
package azure
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
|
||||
"git.kebcloud.no/keb/lazysecrets/internal/provider"
|
||||
)
|
||||
|
||||
@@ -40,11 +45,8 @@ func (p *Provider) Account() (provider.Account, bool) {
|
||||
user += " (service principal)"
|
||||
}
|
||||
add("user", user)
|
||||
tenant := a.tenant
|
||||
if a.tenantName != "" {
|
||||
tenant = a.tenantName + " · " + a.tenant
|
||||
}
|
||||
add("tenant", tenant)
|
||||
add("tenant", a.tenantName)
|
||||
add("tenant ID", a.tenant)
|
||||
if oid := str("oid"); oid != "" {
|
||||
add("object ID", oid)
|
||||
out.Copy, out.CopyLabel = oid, "object ID"
|
||||
@@ -158,3 +160,68 @@ func directoryRoles(wids any) string {
|
||||
slices.Sort(names)
|
||||
return strings.Join(names, ", ")
|
||||
}
|
||||
|
||||
// graphGroups lists the signed-in user's groups, nested ones too, from
|
||||
// Microsoft Graph with az's token for it.
|
||||
const graphGroups = "https://graph.microsoft.com/v1.0/me/transitiveMemberOf?$select=displayName&$top=999"
|
||||
|
||||
var _ provider.AccountGroups = (*Provider)(nil)
|
||||
|
||||
// Groups lists the groups the account is a member of, by name.
|
||||
func (p *Provider) Groups(ctx context.Context) ([]string, error) {
|
||||
if p.account == nil || (p.account.cloud != "" && p.account.cloud != "AzureCloud") {
|
||||
return nil, provider.ErrUnsupported
|
||||
}
|
||||
tok, err := p.cred.GetToken(ctx, policy.TokenRequestOptions{Scopes: []string{"https://graph.microsoft.com/.default"}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var names []string
|
||||
for next := graphGroups; next != ""; {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, next, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+tok.Token)
|
||||
resp, err := p.http().Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var page struct {
|
||||
Value []struct {
|
||||
Type string `json:"@odata.type"`
|
||||
Name string `json:"displayName"`
|
||||
} `json:"value"`
|
||||
Next string `json:"@odata.nextLink"`
|
||||
Error *struct {
|
||||
Message string `json:"message"`
|
||||
} `json:"error"`
|
||||
}
|
||||
err = json.NewDecoder(resp.Body).Decode(&page)
|
||||
resp.Body.Close()
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, fmt.Errorf("listing groups from Microsoft Graph: %w", err)
|
||||
case page.Error != nil:
|
||||
return nil, fmt.Errorf("listing groups from Microsoft Graph: %s", page.Error.Message)
|
||||
case resp.StatusCode != http.StatusOK:
|
||||
return nil, fmt.Errorf("listing groups from Microsoft Graph: %s", resp.Status)
|
||||
}
|
||||
for _, v := range page.Value {
|
||||
if v.Type == "#microsoft.graph.group" && v.Name != "" {
|
||||
names = append(names, v.Name)
|
||||
}
|
||||
}
|
||||
next = page.Next
|
||||
}
|
||||
slices.SortFunc(names, func(a, b string) int { return strings.Compare(strings.ToLower(a), strings.ToLower(b)) })
|
||||
return names, nil
|
||||
}
|
||||
|
||||
// http is the client for Microsoft Graph; tests replace it.
|
||||
func (p *Provider) http() *http.Client {
|
||||
if p.graphClient != nil {
|
||||
return p.graphClient
|
||||
}
|
||||
return http.DefaultClient
|
||||
}
|
||||
Reference in new issue
Block a user