@@ -5,9 +5,10 @@ import (
55 "encoding/json"
66 "fmt"
77
8- "github.com/inconshreveable/log15 "
8+ sglog "github.com/sourcegraph/log "
99
1010 "github.com/sourcegraph/sourcegraph/internal/actor"
11+ "github.com/sourcegraph/sourcegraph/internal/auth"
1112 "github.com/sourcegraph/sourcegraph/internal/authz"
1213 "github.com/sourcegraph/sourcegraph/internal/database"
1314 "github.com/sourcegraph/sourcegraph/internal/deviceid"
@@ -62,14 +63,15 @@ func GetAndSaveUser(ctx context.Context, db database.DB, op GetAndSaveUserOp) (u
6263 return MockGetAndSaveUser (ctx , op )
6364 }
6465
65- extacc := db .UserExternalAccounts ()
66+ externalAccountsStore := db .UserExternalAccounts ()
67+ logger := sglog .Scoped ("authGetAndSaveUser" , "get and save user authenticated by external providers" )
6668
6769 userID , userSaved , extAcctSaved , safeErrMsg , err := func () (int32 , bool , bool , string , error ) {
6870 if actor := actor .FromContext (ctx ); actor .IsAuthenticated () {
6971 return actor .UID , false , false , "" , nil
7072 }
7173
72- uid , lookupByExternalErr := extacc .LookupUserAndSave (ctx , op .ExternalAccount , op .ExternalAccountData )
74+ uid , lookupByExternalErr := externalAccountsStore .LookupUserAndSave (ctx , op .ExternalAccount , op .ExternalAccountData )
7375 if lookupByExternalErr == nil {
7476 return uid , false , true , "" , nil
7577 }
@@ -104,8 +106,18 @@ func GetAndSaveUser(ctx context.Context, db database.DB, op GetAndSaveUserOp) (u
104106 return 0 , false , false , "It looks like this is your first time signing in with this external identity. Sourcegraph couldn't link it to an existing user, because no verified email was provided. Ask your site admin to configure the auth provider to include the user's verified email on sign-in." , lookupByExternalErr
105107 }
106108
107- // If CreateIfNotExist is true, create the new user, regardless of whether the email was verified or not.
108- userID , err := extacc .CreateUserAndSave (ctx , op .UserProps , op .ExternalAccount , op .ExternalAccountData )
109+ act := & actor.Actor {
110+ SourcegraphOperator : op .ExternalAccount .ServiceType == auth .SourcegraphOperatorProviderType ,
111+ }
112+
113+ // If CreateIfNotExist is true, create the new user, regardless of whether the
114+ // email was verified or not.
115+ //
116+ // NOTE: It is important to propagate the correct context that carries the
117+ // information of the actor, especially whether the actor is a Sourcegraph
118+ // operator or not.
119+ ctx = actor .WithActor (ctx , act )
120+ userID , err := externalAccountsStore .CreateUserAndSave (ctx , op .UserProps , op .ExternalAccount , op .ExternalAccountData )
109121 switch {
110122 case database .IsUsernameExists (err ):
111123 return 0 , false , false , fmt .Sprintf ("Username %q already exists, but no verified email matched %q" , op .UserProps .Username , op .UserProps .Email ), err
@@ -114,26 +126,68 @@ func GetAndSaveUser(ctx context.Context, db database.DB, op GetAndSaveUserOp) (u
114126 case err != nil :
115127 return 0 , false , false , "Unable to create a new user account due to a unexpected error. Ask a site admin for help." , err
116128 }
129+ act .UID = userID
117130
118131 if err = db .Authz ().GrantPendingPermissions (ctx , & database.GrantPendingPermissionsArgs {
119132 UserID : userID ,
120133 Perm : authz .Read ,
121134 Type : authz .PermRepos ,
122135 }); err != nil {
123- log15 .Error ("Failed to grant user pending permissions" , "userID" , userID , "error" , err )
136+ logger .Error (
137+ "failed to grant user pending permissions" ,
138+ sglog .Int32 ("userID" , userID ),
139+ sglog .Error (err ),
140+ )
141+ // OK to continue, since this is a best-effort to improve the UX with some initial permissions available.
124142 }
125143
126- serviceTypeArg := json .RawMessage (fmt .Sprintf (`{"serviceType": %q}` , op .ExternalAccount .ServiceType ))
127- if logErr := usagestats .LogBackendEvent (db , actor .FromContext (ctx ).UID , deviceid .FromContext (ctx ), "ExternalAuthSignupSucceeded" , serviceTypeArg , serviceTypeArg , featureflag .GetEvaluatedFlagSet (ctx ), nil ); logErr != nil {
128- log15 .Warn ("Failed to log event ExternalAuthSignupSucceded" , "error" , logErr )
144+ const eventName = "ExternalAuthSignupSucceeded"
145+ args , err := json .Marshal (map [string ]any {
146+ // NOTE: The conventional name should be "service_type", but keeping as-is for
147+ // backwards capability.
148+ "serviceType" : op .ExternalAccount .ServiceType ,
149+ })
150+ if err != nil {
151+ logger .Error (
152+ "failed to marshal JSON for event log argument" ,
153+ sglog .String ("eventName" , eventName ),
154+ sglog .Error (err ),
155+ )
156+ // OK to continue, we still want the event log to be created
157+ }
158+
159+ // NOTE: It is important to propagate the correct context that carries the
160+ // information of the actor, especially whether the actor is a Sourcegraph
161+ // operator or not.
162+ err = usagestats .LogEvent (
163+ ctx ,
164+ db ,
165+ usagestats.Event {
166+ EventName : eventName ,
167+ UserID : act .UID ,
168+ Argument : args ,
169+ Source : "BACKEND" ,
170+ },
171+ )
172+ if err != nil {
173+ logger .Error (
174+ "failed to log event" ,
175+ sglog .String ("eventName" , eventName ),
176+ sglog .Error (err ),
177+ )
129178 }
130179
131180 return userID , true , true , "" , nil
132181 }()
133182 if err != nil {
183+ const eventName = "ExternalAuthSignupFailed"
134184 serviceTypeArg := json .RawMessage (fmt .Sprintf (`{"serviceType": %q}` , op .ExternalAccount .ServiceType ))
135- if logErr := usagestats .LogBackendEvent (db , actor .FromContext (ctx ).UID , deviceid .FromContext (ctx ), "ExternalAuthSignupFailed" , serviceTypeArg , serviceTypeArg , featureflag .GetEvaluatedFlagSet (ctx ), nil ); logErr != nil {
136- log15 .Warn ("Failed to log event ExternalAuthSignUpFailed" , "error" , logErr )
185+ if logErr := usagestats .LogBackendEvent (db , actor .FromContext (ctx ).UID , deviceid .FromContext (ctx ), eventName , serviceTypeArg , serviceTypeArg , featureflag .GetEvaluatedFlagSet (ctx ), nil ); logErr != nil {
186+ logger .Error (
187+ "failed to log event" ,
188+ sglog .String ("eventName" , eventName ),
189+ sglog .Error (err ),
190+ )
137191 }
138192 return 0 , safeErrMsg , err
139193 }
@@ -162,7 +216,7 @@ func GetAndSaveUser(ctx context.Context, db database.DB, op GetAndSaveUserOp) (u
162216
163217 // Create/update the external account and ensure it's associated with the user ID
164218 if ! extAcctSaved {
165- err := extacc .AssociateUserAndSave (ctx , userID , op .ExternalAccount , op .ExternalAccountData )
219+ err := externalAccountsStore .AssociateUserAndSave (ctx , userID , op .ExternalAccount , op .ExternalAccountData )
166220 if err != nil {
167221 return 0 , "Unexpected error associating the external account with your Sourcegraph user. The most likely cause for this problem is that another Sourcegraph user is already linked with this external account. A site admin or the other user can unlink the account to fix this problem." , err
168222 }
@@ -172,7 +226,12 @@ func GetAndSaveUser(ctx context.Context, db database.DB, op GetAndSaveUserOp) (u
172226 Perm : authz .Read ,
173227 Type : authz .PermRepos ,
174228 }); err != nil {
175- log15 .Error ("Failed to grant user pending permissions" , "userID" , userID , "error" , err )
229+ logger .Error (
230+ "failed to grant user pending permissions" ,
231+ sglog .Int32 ("userID" , userID ),
232+ sglog .Error (err ),
233+ )
234+ // OK to continue, since this is a best-effort to improve the UX with some initial permissions available.
176235 }
177236 }
178237
0 commit comments