mirror of
https://github.com/grafana/grafana.git
synced 2025-01-21 22:13:38 -06:00
CloudMigration: Create authapi service (#96581)
This commit is contained in:
parent
c8bc1f8637
commit
e9fae5bd7f
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@ -664,6 +664,7 @@ playwright.config.ts @grafana/plugins-platform-frontend
|
||||
/pkg/services/caching/ @grafana/grafana-operator-experience-squad
|
||||
/pkg/services/cloudmigration/ @grafana/grafana-operator-experience-squad
|
||||
/pkg/services/gcom/ @grafana/grafana-operator-experience-squad
|
||||
/pkg/services/authapi/ @grafana/grafana-operator-experience-squad
|
||||
|
||||
# Feature toggles
|
||||
/pkg/services/featuremgmt/ @grafana/grafana-backend-services-squad
|
||||
|
@ -188,6 +188,7 @@ Experimental features might be changed or removed without prior notice.
|
||||
| `kubernetesFeatureToggles` | Use the kubernetes API for feature toggle management in the frontend |
|
||||
| `newFolderPicker` | Enables the nested folder picker without having nested folders enabled |
|
||||
| `onPremToCloudMigrationsAlerts` | Enables the migration of alerts and its child resources to your Grafana Cloud stack. Requires `onPremToCloudMigrations` to be enabled in conjunction. |
|
||||
| `onPremToCloudMigrationsAuthApiMig` | Enables the use of auth api instead of gcom for internal token services. Requires `onPremToCloudMigrations` to be enabled in conjunction. |
|
||||
| `sqlExpressions` | Enables using SQL and DuckDB functions as Expressions. |
|
||||
| `nodeGraphDotLayout` | Changed the layout algorithm for the node graph |
|
||||
| `kubernetesAggregator` | Enable grafana's embedded kube-aggregator |
|
||||
|
@ -158,6 +158,7 @@ export interface FeatureToggles {
|
||||
jitterAlertRulesWithinGroups?: boolean;
|
||||
onPremToCloudMigrations?: boolean;
|
||||
onPremToCloudMigrationsAlerts?: boolean;
|
||||
onPremToCloudMigrationsAuthApiMig?: boolean;
|
||||
alertingSaveStatePeriodic?: boolean;
|
||||
promQLScope?: boolean;
|
||||
logQLScope?: boolean;
|
||||
|
398
pkg/services/authapi/authapi.go
Normal file
398
pkg/services/authapi/authapi.go
Normal file
@ -0,0 +1,398 @@
|
||||
// Package authapi contains the connector for Grafana internal auth service. This can be used instead of the GCOM service
|
||||
// to create access policies and access tokens
|
||||
package authapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
)
|
||||
|
||||
const LogPrefix = "auth-api.service"
|
||||
|
||||
var ErrTokenNotFound = errors.New("auth-api: token not found")
|
||||
|
||||
type Service interface {
|
||||
CreateAccessPolicy(ctx context.Context, params CreateAccessPolicyParams, payload CreateAccessPolicyPayload) (AccessPolicy, error)
|
||||
ListAccessPolicies(ctx context.Context, params ListAccessPoliciesParams) ([]AccessPolicy, error)
|
||||
DeleteAccessPolicy(ctx context.Context, params DeleteAccessPolicyParams) (bool, error)
|
||||
ListTokens(ctx context.Context, params ListTokenParams) ([]TokenView, error)
|
||||
CreateToken(ctx context.Context, params CreateTokenParams, payload CreateTokenPayload) (Token, error)
|
||||
DeleteToken(ctx context.Context, params DeleteTokenParams) error
|
||||
}
|
||||
|
||||
type CreateAccessPolicyParams struct {
|
||||
RequestID string
|
||||
// this is needed until we fully migrate from gcom to authapi
|
||||
Region string
|
||||
}
|
||||
|
||||
type CreateAccessPolicyPayload struct {
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Realms []Realm `json:"realms"`
|
||||
Scopes []string `json:"scopes"`
|
||||
}
|
||||
|
||||
type Realm struct {
|
||||
Identifier string `json:"identifier"`
|
||||
LabelPolicies []LabelPolicy `json:"labelPolicies"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type LabelPolicy struct {
|
||||
Selector string `json:"selector"`
|
||||
}
|
||||
|
||||
type createAccessPolicyResponse struct {
|
||||
Data AccessPolicy `json:"data"`
|
||||
}
|
||||
|
||||
type AccessPolicy struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type ListAccessPoliciesParams struct {
|
||||
RequestID string
|
||||
Name string
|
||||
// this is needed until we fully migrate from gcom to authapi
|
||||
Region string
|
||||
}
|
||||
|
||||
type listAccessPoliciesResponse struct {
|
||||
Data []AccessPolicy `json:"data"`
|
||||
}
|
||||
|
||||
type DeleteAccessPolicyParams struct {
|
||||
RequestID string
|
||||
AccessPolicyID string
|
||||
// this is needed until we fully migrate from gcom to authapi
|
||||
Region string
|
||||
}
|
||||
|
||||
type ListTokenParams struct {
|
||||
RequestID string
|
||||
AccessPolicyName string
|
||||
TokenName string
|
||||
// this is needed until we fully migrate from gcom to authapi
|
||||
Region string
|
||||
}
|
||||
|
||||
type CreateTokenParams struct {
|
||||
RequestID string
|
||||
// this is needed until we fully migrate from gcom to authapi
|
||||
Region string
|
||||
}
|
||||
|
||||
type CreateTokenPayload struct {
|
||||
AccessPolicyID string `json:"accessPolicyId"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Name string `json:"name"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
}
|
||||
|
||||
type createTokenResponse struct {
|
||||
Data Token `json:"data"`
|
||||
}
|
||||
|
||||
// Token returned by authapi api when a token gets created.
|
||||
type Token struct {
|
||||
ID string `json:"id"`
|
||||
AccessPolicyID string `json:"accessPolicyId"`
|
||||
Name string `json:"name"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type DeleteTokenParams struct {
|
||||
RequestID string
|
||||
TokenID string
|
||||
// this is needed until we fully migrate from gcom to authapi
|
||||
Region string
|
||||
}
|
||||
|
||||
// TokenView returned by authapi api for a GET token request.
|
||||
type TokenView struct {
|
||||
ID string `json:"id"`
|
||||
AccessPolicyID string `json:"accessPolicyId"`
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"displayName"`
|
||||
ExpiresAt string `json:"expiresAt"`
|
||||
FirstUsedAt string `json:"firstUsedAt"`
|
||||
LastUsedAt string `json:"lastUsedAt"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
}
|
||||
|
||||
type listTokensResponse struct {
|
||||
Data []TokenView `json:"data"`
|
||||
}
|
||||
|
||||
var _ Service = (*AuthApiClient)(nil)
|
||||
|
||||
type AuthApiClient struct {
|
||||
log log.Logger
|
||||
cfg Config
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
ApiURL string
|
||||
Token string
|
||||
}
|
||||
|
||||
func New(cfg Config, httpClient *http.Client) Service {
|
||||
return &AuthApiClient{
|
||||
log: log.New(LogPrefix),
|
||||
cfg: cfg,
|
||||
httpClient: httpClient,
|
||||
}
|
||||
}
|
||||
|
||||
func (client *AuthApiClient) CreateAccessPolicy(ctx context.Context, params CreateAccessPolicyParams, payload CreateAccessPolicyPayload) (AccessPolicy, error) {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/accesspolicies")
|
||||
if err != nil {
|
||||
return AccessPolicy{}, fmt.Errorf("building authapi access policy url: %w", err)
|
||||
}
|
||||
|
||||
body, err := json.Marshal(&payload)
|
||||
if err != nil {
|
||||
return AccessPolicy{}, fmt.Errorf("marshaling request body: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return AccessPolicy{}, fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return AccessPolicy{}, fmt.Errorf("sending http request to create access policy: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return AccessPolicy{}, fmt.Errorf("unexpected response when creating access policy: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
var capResp createAccessPolicyResponse
|
||||
if err := json.NewDecoder(response.Body).Decode(&capResp); err != nil {
|
||||
return AccessPolicy{}, fmt.Errorf("unmarshaling response body: %w", err)
|
||||
}
|
||||
|
||||
return capResp.Data, nil
|
||||
}
|
||||
|
||||
func (client *AuthApiClient) DeleteAccessPolicy(ctx context.Context, params DeleteAccessPolicyParams) (bool, error) {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/accesspolicies/", params.AccessPolicyID)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("building authapi access policy url: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodDelete, endpoint, nil)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("sending http request to create access policy: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode == http.StatusNotFound {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if response.StatusCode == http.StatusOK || response.StatusCode == http.StatusNoContent {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return false, fmt.Errorf("unexpected response when deleting access policy: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
func (client *AuthApiClient) ListAccessPolicies(ctx context.Context, params ListAccessPoliciesParams) ([]AccessPolicy, error) {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/accesspolicies")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building authapi access policy url: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("name", params.Name)
|
||||
request.URL.RawQuery = query.Encode()
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Accept", "application/json")
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sending http request to create access policy: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return nil, fmt.Errorf("unexpected response when listing access policies: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
var lapResp listAccessPoliciesResponse
|
||||
if err := json.NewDecoder(response.Body).Decode(&lapResp); err != nil {
|
||||
return lapResp.Data, fmt.Errorf("unmarshaling response body: %w", err)
|
||||
}
|
||||
return lapResp.Data, nil
|
||||
}
|
||||
|
||||
func (client *AuthApiClient) ListTokens(ctx context.Context, params ListTokenParams) ([]TokenView, error) {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/tokens")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building authapi tokens url: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("accessPolicyName", params.AccessPolicyName)
|
||||
query.Set("name", params.TokenName)
|
||||
|
||||
request.URL.RawQuery = query.Encode()
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sending http request to list access tokens: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return nil, fmt.Errorf("unexpected response when fetching access tokens: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
var body listTokensResponse
|
||||
if err := json.NewDecoder(response.Body).Decode(&body); err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling response body: %w", err)
|
||||
}
|
||||
return body.Data, nil
|
||||
}
|
||||
|
||||
func (client *AuthApiClient) CreateToken(ctx context.Context, params CreateTokenParams, payload CreateTokenPayload) (Token, error) {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/tokens")
|
||||
if err != nil {
|
||||
return Token{}, fmt.Errorf("building authapi tokens url: %w", err)
|
||||
}
|
||||
|
||||
body, err := json.Marshal(&payload)
|
||||
if err != nil {
|
||||
return Token{}, fmt.Errorf("marshaling request body: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return Token{}, fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return Token{}, fmt.Errorf("sending http request to create access token: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return Token{}, fmt.Errorf("unexpected response when creating access token: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
var ctResp createTokenResponse
|
||||
if err := json.NewDecoder(response.Body).Decode(&ctResp); err != nil {
|
||||
return Token{}, fmt.Errorf("unmarshaling response body: %w", err)
|
||||
}
|
||||
|
||||
return ctResp.Data, nil
|
||||
}
|
||||
|
||||
func (client *AuthApiClient) DeleteToken(ctx context.Context, params DeleteTokenParams) error {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/tokens", params.TokenID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building authapi tokens url: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodDelete, endpoint, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return fmt.Errorf("sending http request to delete access token: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode == http.StatusNotFound {
|
||||
return fmt.Errorf("token id: %s %w", params.TokenID, ErrTokenNotFound)
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusOK && response.StatusCode != http.StatusNoContent {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return fmt.Errorf("unexpected response when deleting access token: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
69
pkg/services/authapi/fake/authapistub.go
Normal file
69
pkg/services/authapi/fake/authapistub.go
Normal file
@ -0,0 +1,69 @@
|
||||
package fake
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/authapi"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
var _ authapi.Service = (*AuthapiStub)(nil)
|
||||
|
||||
type AuthapiStub struct {
|
||||
// The cloud migration token created by this stub.
|
||||
Token *authapi.TokenView
|
||||
Policies map[string]authapi.AccessPolicy
|
||||
}
|
||||
|
||||
func (client *AuthapiStub) CreateAccessPolicy(_ context.Context, _ authapi.CreateAccessPolicyParams, _ authapi.CreateAccessPolicyPayload) (authapi.AccessPolicy, error) {
|
||||
randStr := fmt.Sprintf("random-policy-%s", util.GenerateShortUID())
|
||||
policy := authapi.AccessPolicy{
|
||||
ID: randStr,
|
||||
Name: randStr,
|
||||
}
|
||||
client.Policies[policy.ID] = policy
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func (client *AuthapiStub) DeleteAccessPolicy(_ context.Context, params authapi.DeleteAccessPolicyParams) (bool, error) {
|
||||
delete(client.Policies, params.AccessPolicyID)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (client *AuthapiStub) ListAccessPolicies(_ context.Context, _ authapi.ListAccessPoliciesParams) ([]authapi.AccessPolicy, error) {
|
||||
items := make([]authapi.AccessPolicy, 0)
|
||||
for _, v := range client.Policies {
|
||||
items = append(items, v)
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (client *AuthapiStub) ListTokens(_ context.Context, _ authapi.ListTokenParams) ([]authapi.TokenView, error) {
|
||||
if client.Token == nil {
|
||||
return []authapi.TokenView{}, nil
|
||||
}
|
||||
|
||||
return []authapi.TokenView{*client.Token}, nil
|
||||
}
|
||||
|
||||
func (client *AuthapiStub) CreateToken(_ context.Context, _ authapi.CreateTokenParams, payload authapi.CreateTokenPayload) (authapi.Token, error) {
|
||||
token := authapi.Token{
|
||||
ID: fmt.Sprintf("random-token-%s", util.GenerateShortUID()),
|
||||
Name: payload.Name,
|
||||
AccessPolicyID: payload.AccessPolicyID,
|
||||
Token: fmt.Sprintf("completely_fake_token_%s", util.GenerateShortUID()),
|
||||
}
|
||||
client.Token = &authapi.TokenView{
|
||||
ID: token.ID,
|
||||
Name: token.Name,
|
||||
AccessPolicyID: token.AccessPolicyID,
|
||||
DisplayName: token.Name,
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (client *AuthapiStub) DeleteToken(_ context.Context, _ authapi.DeleteTokenParams) error {
|
||||
client.Token = nil
|
||||
return nil
|
||||
}
|
@ -3,13 +3,13 @@ package cloudmigration
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/gcom"
|
||||
"github.com/grafana/grafana/pkg/services/authapi"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
// GetToken Returns the cloud migration token if it exists.
|
||||
GetToken(ctx context.Context) (gcom.TokenView, error)
|
||||
GetToken(ctx context.Context) (authapi.TokenView, error)
|
||||
// CreateToken Creates a cloud migration token.
|
||||
CreateToken(ctx context.Context) (CreateAccessTokenResponse, error)
|
||||
// ValidateToken Sends a request to GMS to test the token.
|
||||
|
@ -19,6 +19,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/kvstore"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/authapi"
|
||||
"github.com/grafana/grafana/pkg/services/authapi/fake"
|
||||
"github.com/grafana/grafana/pkg/services/cloudmigration"
|
||||
"github.com/grafana/grafana/pkg/services/cloudmigration/api"
|
||||
"github.com/grafana/grafana/pkg/services/cloudmigration/gmsclient"
|
||||
@ -62,6 +64,7 @@ type Service struct {
|
||||
|
||||
dsService datasources.DataSourceService
|
||||
gcomService gcom.Service
|
||||
authApiService authapi.Service
|
||||
dashboardService dashboards.DashboardService
|
||||
folderService folder.Service
|
||||
pluginStore pluginstore.Store
|
||||
@ -151,9 +154,23 @@ func ProvideService(
|
||||
return nil, fmt.Errorf("creating http client for GCOM: %w", err)
|
||||
}
|
||||
s.gcomService = gcom.New(gcom.Config{ApiURL: cfg.GrafanaComAPIURL, Token: cfg.CloudMigration.GcomAPIToken}, httpClientGcom)
|
||||
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagOnPremToCloudMigrationsAuthApiMig) {
|
||||
s.log.Info("using authapi client because feature flag is enabled")
|
||||
httpClientAuthApi, err := httpClientProvider.New()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating http client for AuthApi: %w", err)
|
||||
}
|
||||
// the api token is the same as for gcom
|
||||
s.authApiService = authapi.New(authapi.Config{ApiURL: cfg.CloudMigration.AuthAPIUrl, Token: cfg.CloudMigration.GcomAPIToken}, httpClientAuthApi)
|
||||
} else {
|
||||
s.log.Info("using gcom client for auth")
|
||||
s.authApiService = gcom.New(gcom.Config{ApiURL: cfg.GrafanaComAPIURL, Token: cfg.CloudMigration.GcomAPIToken}, httpClientGcom).(*gcom.GcomClient)
|
||||
}
|
||||
} else {
|
||||
s.gmsClient = gmsclient.NewInMemoryClient()
|
||||
s.gcomService = &gcomStub{policies: map[string]gcom.AccessPolicy{}, token: nil}
|
||||
s.gcomService = &gcomStub{}
|
||||
s.authApiService = &fake.AuthapiStub{Policies: map[string]authapi.AccessPolicy{}, Token: nil}
|
||||
s.cfg.StackID = "12345"
|
||||
}
|
||||
|
||||
@ -169,7 +186,7 @@ func ProvideService(
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetToken(ctx context.Context) (gcom.TokenView, error) {
|
||||
func (s *Service) GetToken(ctx context.Context) (authapi.TokenView, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "CloudMigrationService.GetToken")
|
||||
defer span.End()
|
||||
logger := s.log.FromContext(ctx)
|
||||
@ -179,7 +196,7 @@ func (s *Service) GetToken(ctx context.Context) (gcom.TokenView, error) {
|
||||
defer cancel()
|
||||
instance, err := s.gcomService.GetInstanceByID(timeoutCtx, requestID, s.cfg.StackID)
|
||||
if err != nil {
|
||||
return gcom.TokenView{}, fmt.Errorf("fetching instance by id: id=%s %w", s.cfg.StackID, err)
|
||||
return authapi.TokenView{}, fmt.Errorf("fetching instance by id: id=%s %w", s.cfg.StackID, err)
|
||||
}
|
||||
|
||||
logger.Info("instance found", "slug", instance.Slug)
|
||||
@ -189,14 +206,14 @@ func (s *Service) GetToken(ctx context.Context) (gcom.TokenView, error) {
|
||||
|
||||
timeoutCtx, cancel = context.WithTimeout(ctx, s.cfg.CloudMigration.ListTokensTimeout)
|
||||
defer cancel()
|
||||
tokens, err := s.gcomService.ListTokens(timeoutCtx, gcom.ListTokenParams{
|
||||
tokens, err := s.authApiService.ListTokens(timeoutCtx, authapi.ListTokenParams{
|
||||
RequestID: requestID,
|
||||
Region: instance.RegionSlug,
|
||||
AccessPolicyName: accessPolicyName,
|
||||
TokenName: accessTokenName,
|
||||
Region: instance.RegionSlug,
|
||||
})
|
||||
if err != nil {
|
||||
return gcom.TokenView{}, fmt.Errorf("listing tokens: %w", err)
|
||||
return authapi.TokenView{}, fmt.Errorf("listing tokens: %w", err)
|
||||
}
|
||||
logger.Info("found access tokens", "num_tokens", len(tokens))
|
||||
|
||||
@ -208,7 +225,7 @@ func (s *Service) GetToken(ctx context.Context) (gcom.TokenView, error) {
|
||||
}
|
||||
|
||||
logger.Info("cloud migration token not found")
|
||||
return gcom.TokenView{}, fmt.Errorf("fetching cloud migration token: instance=%+v accessPolicyName=%s accessTokenName=%s %w",
|
||||
return authapi.TokenView{}, fmt.Errorf("fetching cloud migration token: instance=%+v accessPolicyName=%s accessTokenName=%s %w",
|
||||
instance, accessPolicyName, accessTokenName, cloudmigration.ErrTokenNotFound)
|
||||
}
|
||||
|
||||
@ -231,7 +248,7 @@ func (s *Service) CreateToken(ctx context.Context) (cloudmigration.CreateAccessT
|
||||
|
||||
timeoutCtx, cancel = context.WithTimeout(ctx, s.cfg.CloudMigration.FetchAccessPolicyTimeout)
|
||||
defer cancel()
|
||||
existingAccessPolicy, err := s.findAccessPolicyByName(timeoutCtx, instance.RegionSlug, accessPolicyName)
|
||||
existingAccessPolicy, err := s.findAccessPolicyByName(timeoutCtx, accessPolicyName, instance.RegionSlug)
|
||||
if err != nil {
|
||||
return cloudmigration.CreateAccessTokenResponse{}, fmt.Errorf("fetching access policy by name: name=%s %w", accessPolicyName, err)
|
||||
}
|
||||
@ -239,7 +256,7 @@ func (s *Service) CreateToken(ctx context.Context) (cloudmigration.CreateAccessT
|
||||
if existingAccessPolicy != nil {
|
||||
timeoutCtx, cancel := context.WithTimeout(ctx, s.cfg.CloudMigration.DeleteAccessPolicyTimeout)
|
||||
defer cancel()
|
||||
if _, err := s.gcomService.DeleteAccessPolicy(timeoutCtx, gcom.DeleteAccessPolicyParams{
|
||||
if _, err := s.authApiService.DeleteAccessPolicy(timeoutCtx, authapi.DeleteAccessPolicyParams{
|
||||
RequestID: requestID,
|
||||
AccessPolicyID: existingAccessPolicy.ID,
|
||||
Region: instance.RegionSlug,
|
||||
@ -251,15 +268,15 @@ func (s *Service) CreateToken(ctx context.Context) (cloudmigration.CreateAccessT
|
||||
|
||||
timeoutCtx, cancel = context.WithTimeout(ctx, s.cfg.CloudMigration.CreateAccessPolicyTimeout)
|
||||
defer cancel()
|
||||
accessPolicy, err := s.gcomService.CreateAccessPolicy(timeoutCtx,
|
||||
gcom.CreateAccessPolicyParams{
|
||||
accessPolicy, err := s.authApiService.CreateAccessPolicy(timeoutCtx,
|
||||
authapi.CreateAccessPolicyParams{
|
||||
RequestID: requestID,
|
||||
Region: instance.RegionSlug,
|
||||
},
|
||||
gcom.CreateAccessPolicyPayload{
|
||||
authapi.CreateAccessPolicyPayload{
|
||||
Name: accessPolicyName,
|
||||
DisplayName: accessPolicyDisplayName,
|
||||
Realms: []gcom.Realm{{Type: "stack", Identifier: s.cfg.StackID, LabelPolicies: []gcom.LabelPolicy{}}},
|
||||
Realms: []authapi.Realm{{Type: "stack", Identifier: s.cfg.StackID, LabelPolicies: []authapi.LabelPolicy{}}},
|
||||
Scopes: []string{"cloud-migrations:read", "cloud-migrations:write"},
|
||||
})
|
||||
if err != nil {
|
||||
@ -273,9 +290,12 @@ func (s *Service) CreateToken(ctx context.Context) (cloudmigration.CreateAccessT
|
||||
timeoutCtx, cancel = context.WithTimeout(ctx, s.cfg.CloudMigration.CreateTokenTimeout)
|
||||
defer cancel()
|
||||
|
||||
token, err := s.gcomService.CreateToken(timeoutCtx,
|
||||
gcom.CreateTokenParams{RequestID: requestID, Region: instance.RegionSlug},
|
||||
gcom.CreateTokenPayload{
|
||||
token, err := s.authApiService.CreateToken(timeoutCtx,
|
||||
authapi.CreateTokenParams{
|
||||
RequestID: requestID,
|
||||
Region: instance.RegionSlug,
|
||||
},
|
||||
authapi.CreateTokenPayload{
|
||||
AccessPolicyID: accessPolicy.ID,
|
||||
Name: accessTokenName,
|
||||
DisplayName: accessTokenDisplayName,
|
||||
@ -303,14 +323,14 @@ func (s *Service) CreateToken(ctx context.Context) (cloudmigration.CreateAccessT
|
||||
return cloudmigration.CreateAccessTokenResponse{Token: base64.StdEncoding.EncodeToString(bytes)}, nil
|
||||
}
|
||||
|
||||
func (s *Service) findAccessPolicyByName(ctx context.Context, regionSlug, accessPolicyName string) (*gcom.AccessPolicy, error) {
|
||||
accessPolicies, err := s.gcomService.ListAccessPolicies(ctx, gcom.ListAccessPoliciesParams{
|
||||
func (s *Service) findAccessPolicyByName(ctx context.Context, accessPolicyName string, region string) (*authapi.AccessPolicy, error) {
|
||||
accessPolicies, err := s.authApiService.ListAccessPolicies(ctx, authapi.ListAccessPoliciesParams{
|
||||
RequestID: tracing.TraceIDFromContext(ctx, false),
|
||||
Region: regionSlug,
|
||||
Name: accessPolicyName,
|
||||
Region: region,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing access policies: name=%s region=%s :%w", accessPolicyName, regionSlug, err)
|
||||
return nil, fmt.Errorf("listing access policies: name=%s :%w", accessPolicyName, err)
|
||||
}
|
||||
|
||||
for _, accessPolicy := range accessPolicies {
|
||||
@ -349,10 +369,10 @@ func (s *Service) DeleteToken(ctx context.Context, tokenID string) error {
|
||||
|
||||
timeoutCtx, cancel = context.WithTimeout(ctx, s.cfg.CloudMigration.DeleteTokenTimeout)
|
||||
defer cancel()
|
||||
if err := s.gcomService.DeleteToken(timeoutCtx, gcom.DeleteTokenParams{
|
||||
if err := s.authApiService.DeleteToken(timeoutCtx, authapi.DeleteTokenParams{
|
||||
RequestID: tracing.TraceIDFromContext(ctx, false),
|
||||
Region: instance.RegionSlug,
|
||||
TokenID: tokenID,
|
||||
Region: instance.RegionSlug,
|
||||
}); err != nil && !errors.Is(err, gcom.ErrTokenNotFound) {
|
||||
return fmt.Errorf("deleting cloud migration token: tokenID=%s %w", tokenID, err)
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ package cloudmigrationimpl
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/authapi"
|
||||
"github.com/grafana/grafana/pkg/services/cloudmigration"
|
||||
"github.com/grafana/grafana/pkg/services/gcom"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
@ -13,8 +13,8 @@ type NoopServiceImpl struct{}
|
||||
|
||||
var _ cloudmigration.Service = (*NoopServiceImpl)(nil)
|
||||
|
||||
func (s *NoopServiceImpl) GetToken(ctx context.Context) (gcom.TokenView, error) {
|
||||
return gcom.TokenView{}, cloudmigration.ErrFeatureDisabledError
|
||||
func (s *NoopServiceImpl) GetToken(ctx context.Context) (authapi.TokenView, error) {
|
||||
return authapi.TokenView{}, cloudmigration.ErrFeatureDisabledError
|
||||
}
|
||||
|
||||
func (s *NoopServiceImpl) CreateToken(ctx context.Context) (cloudmigration.CreateAccessTokenResponse, error) {
|
||||
|
@ -6,8 +6,8 @@ import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/authapi"
|
||||
"github.com/grafana/grafana/pkg/services/cloudmigration"
|
||||
"github.com/grafana/grafana/pkg/services/gcom"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
@ -20,11 +20,11 @@ type FakeServiceImpl struct {
|
||||
|
||||
var _ cloudmigration.Service = (*FakeServiceImpl)(nil)
|
||||
|
||||
func (m FakeServiceImpl) GetToken(_ context.Context) (gcom.TokenView, error) {
|
||||
func (m FakeServiceImpl) GetToken(_ context.Context) (authapi.TokenView, error) {
|
||||
if m.ReturnError {
|
||||
return gcom.TokenView{}, fmt.Errorf("mock error")
|
||||
return authapi.TokenView{}, fmt.Errorf("mock error")
|
||||
}
|
||||
return gcom.TokenView{ID: "mock_id", DisplayName: "mock_name"}, nil
|
||||
return authapi.TokenView{ID: "mock_id", DisplayName: "mock_name"}, nil
|
||||
}
|
||||
|
||||
func (m FakeServiceImpl) CreateToken(_ context.Context) (cloudmigration.CreateAccessTokenResponse, error) {
|
||||
|
@ -6,16 +6,12 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/gcom"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
type gcomStub struct {
|
||||
// The cloud migration token created by this stub.
|
||||
token *gcom.TokenView
|
||||
policies map[string]gcom.AccessPolicy
|
||||
}
|
||||
|
||||
func (client *gcomStub) GetInstanceByID(ctx context.Context, requestID string, instanceID string) (gcom.Instance, error) {
|
||||
func (client *gcomStub) GetInstanceByID(_ context.Context, _ string, instanceID string) (gcom.Instance, error) {
|
||||
id, err := strconv.Atoi(instanceID)
|
||||
if err != nil {
|
||||
return gcom.Instance{}, fmt.Errorf("parsing instanceID: %w", err)
|
||||
@ -27,55 +23,3 @@ func (client *gcomStub) GetInstanceByID(ctx context.Context, requestID string, i
|
||||
ClusterSlug: "fake-cluser",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (client *gcomStub) CreateAccessPolicy(ctx context.Context, params gcom.CreateAccessPolicyParams, payload gcom.CreateAccessPolicyPayload) (gcom.AccessPolicy, error) {
|
||||
randStr := fmt.Sprintf("random-policy-%s", util.GenerateShortUID())
|
||||
policy := gcom.AccessPolicy{
|
||||
ID: randStr,
|
||||
Name: randStr,
|
||||
}
|
||||
client.policies[policy.ID] = policy
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func (client *gcomStub) DeleteAccessPolicy(ctx context.Context, params gcom.DeleteAccessPolicyParams) (bool, error) {
|
||||
delete(client.policies, params.AccessPolicyID)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (client *gcomStub) ListAccessPolicies(ctx context.Context, params gcom.ListAccessPoliciesParams) ([]gcom.AccessPolicy, error) {
|
||||
items := make([]gcom.AccessPolicy, 0)
|
||||
for _, v := range client.policies {
|
||||
items = append(items, v)
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (client *gcomStub) ListTokens(ctx context.Context, params gcom.ListTokenParams) ([]gcom.TokenView, error) {
|
||||
if client.token == nil {
|
||||
return []gcom.TokenView{}, nil
|
||||
}
|
||||
|
||||
return []gcom.TokenView{*client.token}, nil
|
||||
}
|
||||
|
||||
func (client *gcomStub) CreateToken(ctx context.Context, params gcom.CreateTokenParams, payload gcom.CreateTokenPayload) (gcom.Token, error) {
|
||||
token := gcom.Token{
|
||||
ID: fmt.Sprintf("random-token-%s", util.GenerateShortUID()),
|
||||
Name: payload.Name,
|
||||
AccessPolicyID: payload.AccessPolicyID,
|
||||
Token: fmt.Sprintf("completely_fake_token_%s", util.GenerateShortUID()),
|
||||
}
|
||||
client.token = &gcom.TokenView{
|
||||
ID: token.ID,
|
||||
Name: token.Name,
|
||||
AccessPolicyID: token.AccessPolicyID,
|
||||
DisplayName: token.Name,
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (client *gcomStub) DeleteToken(ctx context.Context, params gcom.DeleteTokenParams) error {
|
||||
client.token = nil
|
||||
return nil
|
||||
}
|
||||
|
@ -1058,6 +1058,12 @@ var (
|
||||
Stage: FeatureStageExperimental,
|
||||
Owner: grafanaOperatorExperienceSquad,
|
||||
},
|
||||
{
|
||||
Name: "onPremToCloudMigrationsAuthApiMig",
|
||||
Description: "Enables the use of auth api instead of gcom for internal token services. Requires `onPremToCloudMigrations` to be enabled in conjunction.",
|
||||
Stage: FeatureStageExperimental,
|
||||
Owner: grafanaOperatorExperienceSquad,
|
||||
},
|
||||
{
|
||||
Name: "alertingSaveStatePeriodic",
|
||||
Description: "Writes the state periodically to the database, asynchronous to rule evaluation",
|
||||
|
@ -139,6 +139,7 @@ newFolderPicker,experimental,@grafana/grafana-frontend-platform,false,false,true
|
||||
jitterAlertRulesWithinGroups,preview,@grafana/alerting-squad,false,true,false
|
||||
onPremToCloudMigrations,preview,@grafana/grafana-operator-experience-squad,false,false,false
|
||||
onPremToCloudMigrationsAlerts,experimental,@grafana/grafana-operator-experience-squad,false,false,false
|
||||
onPremToCloudMigrationsAuthApiMig,experimental,@grafana/grafana-operator-experience-squad,false,false,false
|
||||
alertingSaveStatePeriodic,privatePreview,@grafana/alerting-squad,false,false,false
|
||||
promQLScope,GA,@grafana/observability-metrics,false,false,false
|
||||
logQLScope,privatePreview,@grafana/observability-logs,false,false,false
|
||||
|
|
@ -567,6 +567,10 @@ const (
|
||||
// Enables the migration of alerts and its child resources to your Grafana Cloud stack. Requires `onPremToCloudMigrations` to be enabled in conjunction.
|
||||
FlagOnPremToCloudMigrationsAlerts = "onPremToCloudMigrationsAlerts"
|
||||
|
||||
// FlagOnPremToCloudMigrationsAuthApiMig
|
||||
// Enables the use of auth api instead of gcom for internal token services. Requires `onPremToCloudMigrations` to be enabled in conjunction.
|
||||
FlagOnPremToCloudMigrationsAuthApiMig = "onPremToCloudMigrationsAuthApiMig"
|
||||
|
||||
// FlagAlertingSaveStatePeriodic
|
||||
// Writes the state periodically to the database, asynchronous to rule evaluation
|
||||
FlagAlertingSaveStatePeriodic = "alertingSaveStatePeriodic"
|
||||
|
@ -2404,6 +2404,18 @@
|
||||
"codeowner": "@grafana/grafana-operator-experience-squad"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "onPremToCloudMigrationsAuthApiMig",
|
||||
"resourceVersion": "1732033809064",
|
||||
"creationTimestamp": "2024-11-19T16:30:09Z"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Enables the use of auth api instead of gcom for internal token services. Requires `onPremToCloudMigrations` to be enabled in conjunction.",
|
||||
"stage": "experimental",
|
||||
"codeowner": "@grafana/grafana-operator-experience-squad"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "openSearchBackendFlowEnabled",
|
||||
|
@ -1,7 +1,6 @@
|
||||
package gcom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@ -9,7 +8,6 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
)
|
||||
@ -20,12 +18,6 @@ var ErrTokenNotFound = errors.New("gcom: token not found")
|
||||
|
||||
type Service interface {
|
||||
GetInstanceByID(ctx context.Context, requestID string, instanceID string) (Instance, error)
|
||||
CreateAccessPolicy(ctx context.Context, params CreateAccessPolicyParams, payload CreateAccessPolicyPayload) (AccessPolicy, error)
|
||||
ListAccessPolicies(ctx context.Context, params ListAccessPoliciesParams) ([]AccessPolicy, error)
|
||||
DeleteAccessPolicy(ctx context.Context, params DeleteAccessPolicyParams) (bool, error)
|
||||
ListTokens(ctx context.Context, params ListTokenParams) ([]TokenView, error)
|
||||
CreateToken(ctx context.Context, params CreateTokenParams, payload CreateTokenPayload) (Token, error)
|
||||
DeleteToken(ctx context.Context, params DeleteTokenParams) error
|
||||
}
|
||||
|
||||
type Instance struct {
|
||||
@ -33,98 +25,7 @@ type Instance struct {
|
||||
Slug string `json:"slug"`
|
||||
RegionSlug string `json:"regionSlug"`
|
||||
ClusterSlug string `json:"clusterSlug"`
|
||||
}
|
||||
|
||||
type CreateAccessPolicyParams struct {
|
||||
RequestID string
|
||||
Region string
|
||||
}
|
||||
|
||||
type CreateAccessPolicyPayload struct {
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Realms []Realm `json:"realms"`
|
||||
Scopes []string `json:"scopes"`
|
||||
}
|
||||
|
||||
type Realm struct {
|
||||
Identifier string `json:"identifier"`
|
||||
LabelPolicies []LabelPolicy `json:"labelPolicies"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type LabelPolicy struct {
|
||||
Selector string `json:"selector"`
|
||||
}
|
||||
|
||||
type AccessPolicy struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type ListAccessPoliciesParams struct {
|
||||
RequestID string
|
||||
Region string
|
||||
Name string
|
||||
}
|
||||
|
||||
type ListAccessPoliciesResponse struct {
|
||||
Items []AccessPolicy `json:"items"`
|
||||
}
|
||||
|
||||
type DeleteAccessPolicyParams struct {
|
||||
RequestID string
|
||||
AccessPolicyID string
|
||||
Region string
|
||||
}
|
||||
|
||||
type ListTokenParams struct {
|
||||
RequestID string
|
||||
Region string
|
||||
AccessPolicyName string
|
||||
TokenName string
|
||||
}
|
||||
|
||||
type CreateTokenParams struct {
|
||||
RequestID string
|
||||
Region string
|
||||
}
|
||||
|
||||
type CreateTokenPayload struct {
|
||||
AccessPolicyID string `json:"accessPolicyId"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Name string `json:"name"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
}
|
||||
|
||||
// The token returned by gcom api when a token gets created.
|
||||
type Token struct {
|
||||
ID string `json:"id"`
|
||||
AccessPolicyID string `json:"accessPolicyId"`
|
||||
Name string `json:"name"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type DeleteTokenParams struct {
|
||||
RequestID string
|
||||
Region string
|
||||
TokenID string
|
||||
}
|
||||
|
||||
// The token returned by gcom api for a GET token request.
|
||||
type TokenView struct {
|
||||
ID string `json:"id"`
|
||||
AccessPolicyID string `json:"accessPolicyId"`
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"displayName"`
|
||||
ExpiresAt string `json:"expiresAt"`
|
||||
FirstUsedAt string `json:"firstUsedAt"`
|
||||
LastUsedAt string `json:"lastUsedAt"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
}
|
||||
|
||||
type listTokensResponse struct {
|
||||
Items []TokenView `json:"items"`
|
||||
OrgId int `json:"orgId"`
|
||||
}
|
||||
|
||||
type GcomClient struct {
|
||||
@ -184,267 +85,3 @@ func (client *GcomClient) GetInstanceByID(ctx context.Context, requestID string,
|
||||
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
func (client *GcomClient) CreateAccessPolicy(ctx context.Context, params CreateAccessPolicyParams, payload CreateAccessPolicyPayload) (AccessPolicy, error) {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/accesspolicies")
|
||||
if err != nil {
|
||||
return AccessPolicy{}, fmt.Errorf("building gcom access policy url: %w", err)
|
||||
}
|
||||
|
||||
body, err := json.Marshal(&payload)
|
||||
if err != nil {
|
||||
return AccessPolicy{}, fmt.Errorf("marshaling request body: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return AccessPolicy{}, fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("region", params.Region)
|
||||
|
||||
request.URL.RawQuery = query.Encode()
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return AccessPolicy{}, fmt.Errorf("sending http request to create access policy: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return AccessPolicy{}, fmt.Errorf("unexpected response when creating access policy: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
var accessPolicy AccessPolicy
|
||||
if err := json.NewDecoder(response.Body).Decode(&accessPolicy); err != nil {
|
||||
return accessPolicy, fmt.Errorf("unmarshaling response body: %w", err)
|
||||
}
|
||||
|
||||
return accessPolicy, nil
|
||||
}
|
||||
|
||||
func (client *GcomClient) DeleteAccessPolicy(ctx context.Context, params DeleteAccessPolicyParams) (bool, error) {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/accesspolicies/", params.AccessPolicyID)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("building gcom access policy url: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodDelete, endpoint, nil)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("region", params.Region)
|
||||
|
||||
request.URL.RawQuery = query.Encode()
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("sending http request to create access policy: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode == http.StatusNotFound {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if response.StatusCode == http.StatusOK || response.StatusCode == http.StatusNoContent {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return false, fmt.Errorf("unexpected response when deleting access policy: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
func (client *GcomClient) ListAccessPolicies(ctx context.Context, params ListAccessPoliciesParams) ([]AccessPolicy, error) {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/accesspolicies")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building gcom access policy url: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("region", params.Region)
|
||||
query.Set("name", params.Name)
|
||||
request.URL.RawQuery = query.Encode()
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Accept", "application/json")
|
||||
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sending http request to create access policy: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return nil, fmt.Errorf("unexpected response when listing access policies: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
var responseBody ListAccessPoliciesResponse
|
||||
if err := json.NewDecoder(response.Body).Decode(&responseBody); err != nil {
|
||||
return responseBody.Items, fmt.Errorf("unmarshaling response body: %w", err)
|
||||
}
|
||||
|
||||
return responseBody.Items, nil
|
||||
}
|
||||
|
||||
func (client *GcomClient) ListTokens(ctx context.Context, params ListTokenParams) ([]TokenView, error) {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/tokens")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building gcom tokens url: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("region", params.Region)
|
||||
query.Set("accessPolicyName", params.AccessPolicyName)
|
||||
query.Set("name", params.TokenName)
|
||||
|
||||
request.URL.RawQuery = query.Encode()
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sending http request to list access tokens: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return nil, fmt.Errorf("unexpected response when fetching access tokens: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
var body listTokensResponse
|
||||
if err := json.NewDecoder(response.Body).Decode(&body); err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling response body: %w", err)
|
||||
}
|
||||
|
||||
return body.Items, nil
|
||||
}
|
||||
|
||||
func (client *GcomClient) CreateToken(ctx context.Context, params CreateTokenParams, payload CreateTokenPayload) (Token, error) {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/tokens")
|
||||
if err != nil {
|
||||
return Token{}, fmt.Errorf("building gcom tokens url: %w", err)
|
||||
}
|
||||
|
||||
body, err := json.Marshal(&payload)
|
||||
if err != nil {
|
||||
return Token{}, fmt.Errorf("marshaling request body: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return Token{}, fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("region", params.Region)
|
||||
|
||||
request.URL.RawQuery = query.Encode()
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return Token{}, fmt.Errorf("sending http request to create access token: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return Token{}, fmt.Errorf("unexpected response when creating access token: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
var token Token
|
||||
if err := json.NewDecoder(response.Body).Decode(&token); err != nil {
|
||||
return token, fmt.Errorf("unmarshaling response body: %w", err)
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (client *GcomClient) DeleteToken(ctx context.Context, params DeleteTokenParams) error {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/tokens", params.TokenID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building gcom tokens url: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodDelete, endpoint, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("region", params.Region)
|
||||
|
||||
request.URL.RawQuery = query.Encode()
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return fmt.Errorf("sending http request to delete access token: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode == http.StatusNotFound {
|
||||
return fmt.Errorf("token id: %s %w", params.TokenID, ErrTokenNotFound)
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusOK && response.StatusCode != http.StatusNoContent {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return fmt.Errorf("unexpected response when deleting access token: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
289
pkg/services/gcom/gcomauth.go
Normal file
289
pkg/services/gcom/gcomauth.go
Normal file
@ -0,0 +1,289 @@
|
||||
package gcom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/authapi"
|
||||
)
|
||||
|
||||
// this will be removed when service in authapi is fully enabled
|
||||
|
||||
type listTokensResponse struct {
|
||||
Items []authapi.TokenView `json:"items"`
|
||||
}
|
||||
|
||||
type listAccessPoliciesResponse struct {
|
||||
Items []authapi.AccessPolicy `json:"items"`
|
||||
}
|
||||
|
||||
var _ authapi.Service = (*GcomClient)(nil)
|
||||
|
||||
func (client *GcomClient) CreateAccessPolicy(ctx context.Context, params authapi.CreateAccessPolicyParams, payload authapi.CreateAccessPolicyPayload) (authapi.AccessPolicy, error) {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/accesspolicies")
|
||||
if err != nil {
|
||||
return authapi.AccessPolicy{}, fmt.Errorf("building gcom access policy url: %w", err)
|
||||
}
|
||||
|
||||
body, err := json.Marshal(&payload)
|
||||
if err != nil {
|
||||
return authapi.AccessPolicy{}, fmt.Errorf("marshaling request body: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return authapi.AccessPolicy{}, fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("region", params.Region)
|
||||
|
||||
request.URL.RawQuery = query.Encode()
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return authapi.AccessPolicy{}, fmt.Errorf("sending http request to create access policy: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return authapi.AccessPolicy{}, fmt.Errorf("unexpected response when creating access policy: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
var accessPolicy authapi.AccessPolicy
|
||||
if err := json.NewDecoder(response.Body).Decode(&accessPolicy); err != nil {
|
||||
return accessPolicy, fmt.Errorf("unmarshaling response body: %w", err)
|
||||
}
|
||||
|
||||
return accessPolicy, nil
|
||||
}
|
||||
|
||||
func (client *GcomClient) DeleteAccessPolicy(ctx context.Context, params authapi.DeleteAccessPolicyParams) (bool, error) {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/accesspolicies/", params.AccessPolicyID)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("building gcom access policy url: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodDelete, endpoint, nil)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("region", params.Region)
|
||||
|
||||
request.URL.RawQuery = query.Encode()
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("sending http request to create access policy: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode == http.StatusNotFound {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if response.StatusCode == http.StatusOK || response.StatusCode == http.StatusNoContent {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return false, fmt.Errorf("unexpected response when deleting access policy: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
func (client *GcomClient) ListAccessPolicies(ctx context.Context, params authapi.ListAccessPoliciesParams) ([]authapi.AccessPolicy, error) {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/accesspolicies")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building gcom access policy url: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("region", params.Region)
|
||||
query.Set("name", params.Name)
|
||||
request.URL.RawQuery = query.Encode()
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Accept", "application/json")
|
||||
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sending http request to create access policy: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return nil, fmt.Errorf("unexpected response when listing access policies: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
var responseBody listAccessPoliciesResponse
|
||||
if err := json.NewDecoder(response.Body).Decode(&responseBody); err != nil {
|
||||
return responseBody.Items, fmt.Errorf("unmarshaling response body: %w", err)
|
||||
}
|
||||
|
||||
return responseBody.Items, nil
|
||||
}
|
||||
|
||||
func (client *GcomClient) ListTokens(ctx context.Context, params authapi.ListTokenParams) ([]authapi.TokenView, error) {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/tokens")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building gcom tokens url: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("region", params.Region)
|
||||
query.Set("accessPolicyName", params.AccessPolicyName)
|
||||
query.Set("name", params.TokenName)
|
||||
|
||||
request.URL.RawQuery = query.Encode()
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sending http request to list access tokens: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return nil, fmt.Errorf("unexpected response when fetching access tokens: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
var body listTokensResponse
|
||||
if err := json.NewDecoder(response.Body).Decode(&body); err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling response body: %w", err)
|
||||
}
|
||||
|
||||
return body.Items, nil
|
||||
}
|
||||
|
||||
func (client *GcomClient) CreateToken(ctx context.Context, params authapi.CreateTokenParams, payload authapi.CreateTokenPayload) (authapi.Token, error) {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/tokens")
|
||||
if err != nil {
|
||||
return authapi.Token{}, fmt.Errorf("building gcom tokens url: %w", err)
|
||||
}
|
||||
|
||||
body, err := json.Marshal(&payload)
|
||||
if err != nil {
|
||||
return authapi.Token{}, fmt.Errorf("marshaling request body: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return authapi.Token{}, fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("region", params.Region)
|
||||
|
||||
request.URL.RawQuery = query.Encode()
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return authapi.Token{}, fmt.Errorf("sending http request to create access token: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return authapi.Token{}, fmt.Errorf("unexpected response when creating access token: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
var token authapi.Token
|
||||
if err := json.NewDecoder(response.Body).Decode(&token); err != nil {
|
||||
return token, fmt.Errorf("unmarshaling response body: %w", err)
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (client *GcomClient) DeleteToken(ctx context.Context, params authapi.DeleteTokenParams) error {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/tokens", params.TokenID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building gcom tokens url: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodDelete, endpoint, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("region", params.Region)
|
||||
|
||||
request.URL.RawQuery = query.Encode()
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return fmt.Errorf("sending http request to delete access token: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode == http.StatusNotFound {
|
||||
return fmt.Errorf("token id: %s %w", params.TokenID, ErrTokenNotFound)
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusOK && response.StatusCode != http.StatusNoContent {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return fmt.Errorf("unexpected response when deleting access token: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -8,6 +8,7 @@ import (
|
||||
type CloudMigrationSettings struct {
|
||||
IsTarget bool
|
||||
GcomAPIToken string
|
||||
AuthAPIUrl string
|
||||
SnapshotFolder string
|
||||
GMSDomain string
|
||||
GMSStartSnapshotTimeout time.Duration
|
||||
@ -33,6 +34,7 @@ func (cfg *Cfg) readCloudMigrationSettings() {
|
||||
cloudMigration := cfg.Raw.Section("cloud_migration")
|
||||
cfg.CloudMigration.IsTarget = cloudMigration.Key("is_target").MustBool(false)
|
||||
cfg.CloudMigration.GcomAPIToken = cloudMigration.Key("gcom_api_token").MustString("")
|
||||
cfg.CloudMigration.AuthAPIUrl = cloudMigration.Key("auth_api_url").MustString("")
|
||||
cfg.CloudMigration.SnapshotFolder = cloudMigration.Key("snapshot_folder").MustString("")
|
||||
cfg.CloudMigration.GMSDomain = cloudMigration.Key("domain").MustString("")
|
||||
cfg.CloudMigration.GMSValidateKeyTimeout = cloudMigration.Key("validate_key_timeout").MustDuration(5 * time.Second)
|
||||
|
Loading…
Reference in New Issue
Block a user