Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ref: Connector cleanup #31

Merged
merged 6 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions cmd/baton-slack/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,21 @@ func main() {
}

func getConnector(ctx context.Context, v *viper.Viper) (types.ConnectorServer, error) {
l := ctxzap.Extract(ctx)
logger := ctxzap.Extract(ctx)
cb, err := connector.New(
ctx,
v.GetString(AccessTokenField.FieldName),
v.GetString(EnterpriseTokenField.FieldName),
v.GetBool(SSOEnabledField.FieldName),
)
if err != nil {
l.Error("error creating connector", zap.Error(err))
logger.Error("error creating connector", zap.Error(err))
return nil, err
}

c, err := connectorbuilder.NewConnector(ctx, cb)
if err != nil {
l.Error("error creating connector", zap.Error(err))
logger.Error("error creating connector", zap.Error(err))
return nil, err
}

Expand Down
38 changes: 37 additions & 1 deletion pkg/slack/models.go → pkg/connector/client/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@ package enterprise

import "github.com/slack-go/slack"

type BaseResponse struct {
Ok bool `json:"ok"`
Error string `json:"error"`
Needed string `json:"needed"`
Provided string `json:"provided"`
}

type Pagination struct {
ResponseMetadata struct {
NextCursor string `json:"next_cursor"`
} `json:"response_metadata"`
}

type SCIMResponse[T any] struct {
Schemas []string `json:"schemas"`
Resources []T `json:"Resources"`
TotalResults int `json:"totalResults"`
ItemsPerPage int `json:"itemsPerPage"`
StartIndex int `json:"startIndex"`
}

type UserAdmin struct {
ID string `json:"id"`
Email string `json:"email"`
Expand Down Expand Up @@ -67,7 +88,7 @@ type EnterpriseUser struct {
Teams []string `json:"teams"`
}

// SCIM resources.
// UserResource SCIM resources.
type UserResource struct {
Schemas []string `json:"schemas"`
ID string `json:"id"`
Expand Down Expand Up @@ -151,3 +172,18 @@ type GroupResource struct {
DisplayName string `json:"displayName"`
Members []Member `json:"members"`
}

type PatchOp struct {
Schemas []string `json:"schemas"`
Operations []ScimOperate `json:"Operations"`
}

type ScimOperate struct {
Op string `json:"op"`
Path string `json:"path"`
Value []UserID `json:"value"`
}

type UserID struct {
Value string `json:"value"`
}
38 changes: 38 additions & 0 deletions pkg/connector/client/path.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package enterprise

import (
"fmt"

"github.com/conductorone/baton-slack/pkg"
)

const (
UrlPathGetRoleAssignments = "/api/admin.roles.listAssignments"
UrlPathGetTeams = "/api/admin.teams.list"
UrlPathGetUserGroupMembers = "/api/usergroups.users.list"
UrlPathGetUserGroups = "/api/usergroups.list"
UrlPathGetUserInfo = "/api/users.info"
UrlPathGetUsers = "/api/users.list"
UrlPathGetUsersAdmin = "/api/admin.users.list"
UrlPathIDPGroup = "/scim/v2/Groups/%s"
UrlPathIDPGroups = "/scim/v2/Groups"
UrlPathSetAdmin = "/api/admin.users.setAdmin"
UrlPathSetOwner = "/api/admin.users.setOwner"
UrlPathSetRegular = "/api/admin.users.setRegular"
baseScimUrl = "https://api.slack.com"
baseUrl = "https://slack.com"
)

func getWorkspaceUrlPathByRole(roleID string) (string, error) {
role, _ := pkg.ParseID(roleID)
switch role {
case "owner":
return UrlPathSetOwner, nil
case "admin":
return UrlPathSetAdmin, nil
case "":
return UrlPathSetRegular, nil
default:
return "", fmt.Errorf("invalid role type: %s", role)
}
}
169 changes: 169 additions & 0 deletions pkg/connector/client/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package enterprise

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

v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2"
"github.com/conductorone/baton-sdk/pkg/uhttp"
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
"go.uber.org/zap"
)

func toValues(queryParameters map[string]interface{}) string {
params := url.Values{}
for key, valueAny := range queryParameters {
switch value := valueAny.(type) {
case string:
params.Add(key, value)
case int:
params.Add(key, strconv.Itoa(value))
case bool:
params.Add(key, strconv.FormatBool(value))
default:
continue
}
}
return params.Encode()
}

func (c *Client) getUrl(
path string,
queryParameters map[string]interface{},
useScim bool,
) *url.URL {
var output *url.URL
if useScim {
output = c.baseScimUrl.JoinPath(path)
} else {
output = c.baseUrl.JoinPath(path)
}

output.RawQuery = toValues(queryParameters)
return output
}

// WithBearerToken - TODO(marcos): move this function to `baton-sdk`.
func WithBearerToken(token string) uhttp.RequestOption {
return uhttp.WithHeader("Authorization", fmt.Sprintf("Bearer %s", token))
}

func (c *Client) post(
ctx context.Context,
path string,
target interface{},
payload map[string]interface{},
useBotToken bool,
) (
*v2.RateLimitDescription,
error,
) {
token := c.token
if useBotToken {
token = c.botToken
}

return c.doRequest(
ctx,
http.MethodPost,
c.getUrl(path, nil, false),
&target,
WithBearerToken(token),
uhttp.WithFormBody(toValues(payload)),
)
}

func (c *Client) getScim(
ctx context.Context,
path string,
target interface{},
queryParameters map[string]interface{},
) (
*v2.RateLimitDescription,
error,
) {
return c.doRequest(
ctx,
http.MethodGet,
c.getUrl(path, queryParameters, true),
&target,
WithBearerToken(c.token),
)
}

func (c *Client) patchScim(
ctx context.Context,
path string,
target interface{},
payload []byte,
) (
*v2.RateLimitDescription,
error,
) {
return c.doRequest(
ctx,
http.MethodPatch,
c.getUrl(path, nil, true),
&target,
WithBearerToken(c.token),
uhttp.WithJSONBody(payload),
)
}

func (c *Client) doRequest(
ctx context.Context,
method string,
url *url.URL,
target interface{},
options ...uhttp.RequestOption,
) (
*v2.RateLimitDescription,
error,
) {
logger := ctxzap.Extract(ctx)
logger.Debug(
"making request",
zap.String("method", method),
zap.String("url", url.String()),
)

options = append(
options,
uhttp.WithAcceptJSONHeader(),
)

request, err := c.wrapper.NewRequest(
ctx,
method,
url,
options...,
)
if err != nil {
return nil, err
}
var ratelimitData v2.RateLimitDescription
response, err := c.wrapper.Do(
request,
uhttp.WithRatelimitData(&ratelimitData),
)
if err != nil {
return &ratelimitData, err
}
defer response.Body.Close()

bodyBytes, err := io.ReadAll(response.Body)
if err != nil {
return &ratelimitData, err
}

if err := json.Unmarshal(bodyBytes, &target); err != nil {
return nil, err
}

return &ratelimitData, nil
}
Loading
Loading