Skip to content

Commit 5d59b88

Browse files
committed
[BB-763] baton-github: add account provisioning
1 parent bbfb49c commit 5d59b88

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed

pkg/connector/connector.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,47 @@ func (gh *GitHub) ResourceSyncers(ctx context.Context) []connectorbuilder.Resour
100100
func (gh *GitHub) Metadata(ctx context.Context) (*v2.ConnectorMetadata, error) {
101101
return &v2.ConnectorMetadata{
102102
DisplayName: "GitHub",
103+
AccountCreationSchema: &v2.ConnectorAccountCreationSchema{
104+
FieldMap: map[string]*v2.ConnectorAccountCreationSchema_Field{
105+
"email": {
106+
DisplayName: "Email",
107+
Description: "This email will be used as the login for the user.",
108+
Field: &v2.ConnectorAccountCreationSchema_Field_StringField{
109+
StringField: &v2.ConnectorAccountCreationSchema_StringField{},
110+
},
111+
Placeholder: "Email",
112+
Order: 1,
113+
},
114+
"role": {
115+
DisplayName: "Role Name",
116+
Description: "role name in organization",
117+
Field: &v2.ConnectorAccountCreationSchema_Field_StringField{
118+
StringField: &v2.ConnectorAccountCreationSchema_StringField{},
119+
},
120+
Placeholder: "role name",
121+
Order: 2,
122+
},
123+
"org": {
124+
DisplayName: "Org Name",
125+
Required: true,
126+
Description: "organization name",
127+
Field: &v2.ConnectorAccountCreationSchema_Field_StringField{
128+
StringField: &v2.ConnectorAccountCreationSchema_StringField{},
129+
},
130+
Placeholder: "organization name",
131+
Order: 3,
132+
},
133+
"userID": {
134+
DisplayName: "github user ID",
135+
Description: "Github User ID",
136+
Field: &v2.ConnectorAccountCreationSchema_Field_StringField{
137+
StringField: &v2.ConnectorAccountCreationSchema_StringField{},
138+
},
139+
Placeholder: "UserID",
140+
Order: 4,
141+
},
142+
},
143+
},
103144
}, nil
104145
}
105146

pkg/connector/user.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2"
1212
"github.com/conductorone/baton-sdk/pkg/annotations"
13+
"github.com/conductorone/baton-sdk/pkg/connectorbuilder"
1314
"github.com/conductorone/baton-sdk/pkg/pagination"
1415
"github.com/conductorone/baton-sdk/pkg/types/resource"
1516
"github.com/google/go-github/v69/github"
@@ -19,6 +20,32 @@ import (
1920
"google.golang.org/protobuf/types/known/timestamppb"
2021
)
2122

23+
func invitationToUserResource(invitation *github.Invitation) (*v2.Resource, error) {
24+
login := invitation.GetLogin()
25+
if login == "" {
26+
login = invitation.GetEmail()
27+
}
28+
29+
ret, err := resource.NewUserResource(
30+
login,
31+
resourceTypeUser,
32+
invitation.GetID(),
33+
[]resource.UserTraitOption{
34+
resource.WithEmail(invitation.GetEmail(), true),
35+
resource.WithUserProfile(map[string]interface{}{
36+
"login": login,
37+
"inviter": invitation.GetInviter().GetLogin(),
38+
}),
39+
resource.WithStatus(v2.UserTrait_Status_STATUS_UNSPECIFIED),
40+
resource.WithUserLogin(login),
41+
},
42+
)
43+
if err != nil {
44+
return nil, err
45+
}
46+
return ret, nil
47+
}
48+
2249
// Create a new connector resource for a GitHub user.
2350
func userResource(ctx context.Context, user *github.User, userEmail string, extraEmails []string) (*v2.Resource, error) {
2451
displayName := user.GetName()
@@ -211,6 +238,107 @@ func (o *userResourceType) List(ctx context.Context, parentID *v2.ResourceId, pt
211238
return rv, pageToken, annotations, nil
212239
}
213240

241+
func (o *userResourceType) CreateAccountCapabilityDetails(ctx context.Context) (*v2.CredentialDetailsAccountProvisioning, annotations.Annotations, error) {
242+
return &v2.CredentialDetailsAccountProvisioning{
243+
SupportedCredentialOptions: []v2.CapabilityDetailCredentialOption{
244+
v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_NO_PASSWORD,
245+
},
246+
PreferredCredentialOption: v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_NO_PASSWORD,
247+
}, nil, nil
248+
}
249+
250+
func (o *userResourceType) CreateAccount(
251+
ctx context.Context,
252+
accountInfo *v2.AccountInfo,
253+
credentialOptions *v2.CredentialOptions,
254+
) (
255+
connectorbuilder.CreateAccountResponse,
256+
[]*v2.PlaintextData,
257+
annotations.Annotations,
258+
error,
259+
) {
260+
params, err := getCreateUserParams(accountInfo)
261+
if err != nil {
262+
return nil, nil, nil, fmt.Errorf("github-connectorv2: failed to get CreateUserParams: %w", err)
263+
}
264+
265+
invitation, resp, err := o.client.Organizations.CreateOrgInvitation(ctx, params.org, &github.CreateOrgInvitationOptions{
266+
InviteeID: params.userID,
267+
Role: params.role,
268+
Email: params.email,
269+
})
270+
if err != nil {
271+
return nil, nil, nil, fmt.Errorf("github-connectorv2: failed to invite user to org: %w", err)
272+
}
273+
274+
restApiRateLimit, err := extractRateLimitData(resp)
275+
if err != nil {
276+
return nil, nil, nil, err
277+
}
278+
279+
var annotations annotations.Annotations
280+
annotations.WithRateLimiting(restApiRateLimit)
281+
282+
r, err := invitationToUserResource(invitation)
283+
if err != nil {
284+
return nil, nil, nil, fmt.Errorf("github-connectorv2: cannot create user resource: %w", err)
285+
}
286+
return &v2.CreateAccountResponse_SuccessResult{
287+
Resource: r,
288+
}, nil, nil, nil
289+
}
290+
291+
type createUserParams struct {
292+
org string
293+
email *string
294+
userID *int64
295+
role *string
296+
}
297+
298+
func getCreateUserParams(accountInfo *v2.AccountInfo) (*createUserParams, error) {
299+
var (
300+
pMap = accountInfo.Profile.AsMap()
301+
uID *int64
302+
role *string
303+
email *string
304+
)
305+
306+
org, ok := pMap["org"].(string)
307+
if !ok || org == "" {
308+
return nil, fmt.Errorf("org is required")
309+
}
310+
311+
e, emailExisted := pMap["email"].(string)
312+
if e != "" {
313+
email = &e
314+
}
315+
316+
userID, userIDExisted := pMap["userID"].(string)
317+
if !emailExisted && !userIDExisted {
318+
return nil, fmt.Errorf("either email or userID should be provided")
319+
}
320+
321+
if userIDExisted {
322+
i, err := strconv.ParseInt(userID, 10, 64)
323+
if err != nil {
324+
return nil, err
325+
}
326+
uID = &i
327+
}
328+
329+
r, _ := pMap["role"].(string)
330+
if r != "" {
331+
role = &r
332+
}
333+
334+
return &createUserParams{
335+
org: org,
336+
email: email,
337+
userID: uID,
338+
role: role,
339+
}, nil
340+
}
341+
214342
func isEmail(email string) bool {
215343
_, err := mail.ParseAddress(email)
216344
return err == nil

0 commit comments

Comments
 (0)