Skip to content

Commit

Permalink
feat(gate): introduce gate provider (#164)
Browse files Browse the repository at this point in the history
* feat(gate): introduce gate provider

* fix: send gate api key in query param

* fix: handle non-success http codes
  • Loading branch information
rahmatrhd authored Aug 1, 2024
1 parent e89f850 commit 46d45ac
Show file tree
Hide file tree
Showing 8 changed files with 715 additions and 3 deletions.
1 change: 1 addition & 0 deletions domain/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const (
ProviderTypePolicyTag = "dataplex"
ProviderTypeShield = "shield"
ProviderTypeGitlab = "gitlab"
ProviderTypeGate = "gate"
)

// Role is the configuration to define a role and mapping the permissions in the provider
Expand Down
7 changes: 4 additions & 3 deletions internal/server/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ package server
import (
"context"

"github.com/goto/guardian/plugins/providers/dataplex"
"github.com/goto/guardian/plugins/providers/gitlab"

"github.com/go-playground/validator/v10"
"github.com/google/uuid"
"github.com/goto/guardian/core"
Expand All @@ -26,8 +23,11 @@ import (
"github.com/goto/guardian/plugins/identities"
"github.com/goto/guardian/plugins/notifiers"
"github.com/goto/guardian/plugins/providers/bigquery"
"github.com/goto/guardian/plugins/providers/dataplex"
"github.com/goto/guardian/plugins/providers/gate"
"github.com/goto/guardian/plugins/providers/gcloudiam"
"github.com/goto/guardian/plugins/providers/gcs"
"github.com/goto/guardian/plugins/providers/gitlab"
"github.com/goto/guardian/plugins/providers/grafana"
"github.com/goto/guardian/plugins/providers/metabase"
"github.com/goto/guardian/plugins/providers/noop"
Expand Down Expand Up @@ -122,6 +122,7 @@ func InitServices(deps ServiceDeps) (*Services, error) {
dataplex.NewProvider(domain.ProviderTypePolicyTag, deps.Crypto),
shield.NewProvider(domain.ProviderTypeShield, deps.Logger),
gitlab.NewProvider(domain.ProviderTypeGitlab, deps.Crypto, deps.Logger),
gate.NewProvider(domain.ProviderTypeGate, deps.Crypto),
}

iamManager := identities.NewManager(deps.Crypto, deps.Validator)
Expand Down
139 changes: 139 additions & 0 deletions pkg/gate/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package gate

import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
)

type Client struct {
baseURL *url.URL
options *options
}

func NewClient(baseURL string, opts ...ClientOption) (*Client, error) {
url, err := url.Parse(baseURL)
if err != nil {
return nil, fmt.Errorf("invalid base URL: %w", err)
}

client := &Client{
baseURL: url,
options: &options{
httpClient: http.DefaultClient,
},
}
for _, o := range opts {
o(client.options)
}
return client, nil
}

type ListGroupsRequest struct {
Page int
PerPage int
}

func (c *Client) ListGroups(ctx context.Context, req *ListGroupsRequest) ([]*Group, *http.Response, error) {
path := "/api/v1/groups"
r, err := c.newRequest(ctx, http.MethodGet, path, nil)
if err != nil {
return nil, nil, err
}

q := r.URL.Query()
if req.Page != 0 {
q.Add("page", strconv.Itoa(req.Page))
}
if req.PerPage != 0 {
q.Add("per_page", strconv.Itoa(req.PerPage))
}
r.URL.RawQuery = q.Encode()

res, err := c.options.httpClient.Do(r)
if err != nil {
return nil, res, err
}

var resBody []*Group
if err := parseResponseBody(res.Body, &resBody); err != nil {
return nil, res, err
}

return resBody, res, nil
}

func (c *Client) AddUserToGroup(ctx context.Context, groupID, userID int) (*http.Response, error) {
path := fmt.Sprintf("/api/v1/groups/%d/users", groupID)
reqBody := map[string]any{"user_id": userID}
r, err := c.newRequest(ctx, http.MethodPost, path, reqBody)
if err != nil {
return nil, err
}

res, err := c.options.httpClient.Do(r)
if err != nil {
return res, err
}

return res, nil
}

func (c *Client) RemoveUserFromGroup(ctx context.Context, groupID, userID int) (*http.Response, error) {
path := fmt.Sprintf("/api/v1/groups/%d/users/%d", groupID, userID)
r, err := c.newRequest(ctx, http.MethodDelete, path, nil)
if err != nil {
return nil, err
}

res, err := c.options.httpClient.Do(r)
if err != nil {
return res, err
}

return res, nil
}

func (c *Client) newRequest(ctx context.Context, method, path string, body interface{}) (*http.Request, error) {
url, err := c.baseURL.Parse(path)
if err != nil {
return nil, err
}

var reqBody io.ReadWriter
if body != nil {
reqBody = new(bytes.Buffer)
if err := json.NewEncoder(reqBody).Encode(body); err != nil {
return nil, err
}
}

req, err := http.NewRequestWithContext(ctx, method, url.String(), reqBody)
if err != nil {
return nil, err
}
req.Header.Add("Content-Type", "application/json")

// auth
if c.options.token != "" {
if c.options.queryParamAuthKey != "" {
q := req.URL.Query()
q.Add(c.options.queryParamAuthKey, c.options.token)
req.URL.RawQuery = q.Encode()
} else {
req.Header.Add("Authorization", c.options.token)
}
}

return req, nil
}

func parseResponseBody(resBody io.ReadCloser, v interface{}) error {
defer resBody.Close()
return json.NewDecoder(resBody).Decode(v)
}
12 changes: 12 additions & 0 deletions pkg/gate/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package gate

type Group struct {
ID int `json:"id"`
Name string `json:"name"`
GID int `json:"gid"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
DeletedBy string `json:"deleted_by"`
DeletedAt string `json:"deleted_at"`
Description *string `json:"description"`
}
31 changes: 31 additions & 0 deletions pkg/gate/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package gate

import (
"net/http"
)

type options struct {
httpClient *http.Client
token string
queryParamAuthKey string
}

type ClientOption func(*options)

func WithHTTPClient(httpClient *http.Client) ClientOption {
return func(opts *options) {
opts.httpClient = httpClient
}
}

func WithAPIKey(token string) ClientOption {
return func(opts *options) {
opts.token = token
}
}

func WithQueryParamAuthMethod() ClientOption {
return func(opts *options) {
opts.queryParamAuthKey = "token"
}
}
136 changes: 136 additions & 0 deletions plugins/providers/gate/mocks/crypto.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 46d45ac

Please sign in to comment.