Skip to content

Commit eb6a06c

Browse files
authored
Merge pull request #84 from ConductorOne/BB763
[BB-763] baton-github: add account provisioning
2 parents bbfb49c + effb989 commit eb6a06c

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed

pkg/connector/connector.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ var (
5656
},
5757
Annotations: v1AnnotationsForResourceType("user"),
5858
}
59+
resourceTypeInvitation = &v2.ResourceType{
60+
Id: "invitation",
61+
DisplayName: "Invitation",
62+
Traits: []v2.ResourceType_Trait{
63+
v2.ResourceType_TRAIT_USER,
64+
},
65+
Annotations: v1AnnotationsForResourceType("invitation"),
66+
}
5967
resourceTypeApiToken = &v2.ResourceType{
6068
Id: "api-key",
6169
DisplayName: "API Key",
@@ -100,6 +108,30 @@ func (gh *GitHub) ResourceSyncers(ctx context.Context) []connectorbuilder.Resour
100108
func (gh *GitHub) Metadata(ctx context.Context) (*v2.ConnectorMetadata, error) {
101109
return &v2.ConnectorMetadata{
102110
DisplayName: "GitHub",
111+
AccountCreationSchema: &v2.ConnectorAccountCreationSchema{
112+
FieldMap: map[string]*v2.ConnectorAccountCreationSchema_Field{
113+
"email": {
114+
DisplayName: "Email",
115+
Required: true,
116+
Description: "This email will be used as the login for the user.",
117+
Field: &v2.ConnectorAccountCreationSchema_Field_StringField{
118+
StringField: &v2.ConnectorAccountCreationSchema_StringField{},
119+
},
120+
Placeholder: "Email",
121+
Order: 1,
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: 2,
132+
},
133+
},
134+
},
103135
}, nil
104136
}
105137

pkg/connector/user.go

Lines changed: 98 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+
resourceTypeInvitation,
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,77 @@ 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+
Email: params.email,
267+
})
268+
if err != nil {
269+
return nil, nil, nil, fmt.Errorf("github-connectorv2: failed to invite user to org: %w", err)
270+
}
271+
272+
restApiRateLimit, err := extractRateLimitData(resp)
273+
if err != nil {
274+
return nil, nil, nil, err
275+
}
276+
277+
var annotations annotations.Annotations
278+
annotations.WithRateLimiting(restApiRateLimit)
279+
280+
r, err := invitationToUserResource(invitation)
281+
if err != nil {
282+
return nil, nil, nil, fmt.Errorf("github-connectorv2: cannot create user resource: %w", err)
283+
}
284+
return &v2.CreateAccountResponse_SuccessResult{
285+
Resource: r,
286+
}, nil, nil, nil
287+
}
288+
289+
type createUserParams struct {
290+
org string
291+
email *string
292+
}
293+
294+
func getCreateUserParams(accountInfo *v2.AccountInfo) (*createUserParams, error) {
295+
pMap := accountInfo.Profile.AsMap()
296+
org, ok := pMap["org"].(string)
297+
if !ok || org == "" {
298+
return nil, fmt.Errorf("org is required")
299+
}
300+
301+
e, emailExisted := pMap["email"].(string)
302+
if !emailExisted || e == "" {
303+
return nil, fmt.Errorf("email is required")
304+
}
305+
306+
return &createUserParams{
307+
org: org,
308+
email: &e,
309+
}, nil
310+
}
311+
214312
func isEmail(email string) bool {
215313
_, err := mail.ParseAddress(email)
216314
return err == nil

0 commit comments

Comments
 (0)