-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change (allowSignup
for the GitHub auth provider) looks good. (And in general, we can make site config changes much more cheaply/cavalierly now because https://github.com/sourcegraph/sourcegraph/issues/965 makes it possible for Sourcegraph to auto-migrate site config when we change the structure of it.)
I added some code review notes, though.
cmd/frontend/auth/user.go
Outdated
|
||
// CreateOrUpdateUser creates or updates a user using the provided information, looking up a user by | ||
// UpdateUser creates or updates a user using the provided information, looking up a user by |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UpdateUser creates or updates a user
meant to remove "creates or"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To me, "UpdateUser" is clearer, since the caller has to specifically pass createIfNotExist=true
if they want creation behavior.
cmd/frontend/auth/user.go
Outdated
// MockCreateOrUpdateUser is used in tests to mock CreateOrUpdateUser. | ||
var MockCreateOrUpdateUser func(db.NewUser, extsvc.ExternalAccountSpec) (int32, error) | ||
// MockUpdateUser is used in tests to mock UpdateUser. | ||
var MockUpdateUser func(db.NewUser, extsvc.ExternalAccountSpec) (int32, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
db.NewUser
struct sounds confusing if it's passed to an "update" func (doesn't make sense to "update" a "new user")
cmd/frontend/auth/user.go
Outdated
@@ -109,3 +77,53 @@ func CreateOrUpdateUser(ctx context.Context, newOrUpdatedUser db.NewUser, extern | |||
} | |||
return user.ID, "", nil | |||
} | |||
|
|||
func createUser(ctx context.Context, newUser db.NewUser, externalAccount extsvc.ExternalAccountSpec, data extsvc.ExternalAccountData) ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't make sense to call this func createUser
(it was extracted from a func called CreateOrUpdateUser
) because it doesn't just create a user. In some cases (the associateUser == true
code path) it updates the user.
WIP'ing/unassigning to do a more extensive refactoring of the auth code to make this update possible. |
6f533ba
to
402c252
Compare
createUserAndSaveErr error | ||
associateUserAndSaveErr error | ||
getByVerifiedEmailErr error | ||
getByIDErr error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getByIDErr
is unused (from structcheck
)
associateUserAndSaveErr error | ||
getByVerifiedEmailErr error | ||
getByIDErr error | ||
updateErr error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updateErr
is unused (from structcheck
)
|
||
type mockParams struct { | ||
userInfos []userInfo | ||
lookupUserAndSaveErr error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lookupUserAndSaveErr
is unused (from structcheck
)
type mockParams struct { | ||
userInfos []userInfo | ||
lookupUserAndSaveErr error | ||
createUserAndSaveErr error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
createUserAndSaveErr
is unused (from structcheck
)
userInfos []userInfo | ||
lookupUserAndSaveErr error | ||
createUserAndSaveErr error | ||
associateUserAndSaveErr error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
associateUserAndSaveErr
is unused (from structcheck
)
lookupUserAndSaveErr error | ||
createUserAndSaveErr error | ||
associateUserAndSaveErr error | ||
getByVerifiedEmailErr error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getByVerifiedEmailErr
is unused (from structcheck
)
af467a3
to
34ad4c6
Compare
cmd/frontend/auth/providers2.go
Outdated
func GetProviderByConfigID(id ProviderConfigID) Provider { | ||
var ps []Provider | ||
if MockProviders != nil { | ||
ps = MockProviders |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ineffectual assignment to ps
(from ineffassign
)
cmd/frontend/auth/providers2.go
Outdated
<-allProvidersRegistered | ||
allProvidersMu.RLock() | ||
defer allProvidersMu.RUnlock() | ||
ps = allProviders |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ineffectual assignment to ps
(from ineffassign
)
close(allProvidersRegistered) | ||
}) | ||
func GetProviderByConfigID(id ProviderConfigID) Provider { | ||
if MockProviders != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unnecessary nil check around range (from megacheck
)
7a5cf69
to
81f6270
Compare
Codecov Report
|
b3842d0
to
a0958b9
Compare
@sqs @nicksnyder PTAL |
Re: recreating all auth.Providers:
If that is true, then it is fine to recreate them all each time. I think the state they maintain is things like the Are there any other things blocked on me? |
cmd/frontend/auth/user.go
Outdated
|
||
// CreateOrUpdateUser creates or updates a user using the provided information, looking up a user by | ||
// the external account provided. | ||
type GetUserOp struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be GetAndSaveUserOp
? GetUserOp
implies to me that the fields within this struct can be incorrect without repercussion, but that is not true.
cmd/frontend/auth/providers.go
Outdated
for _, p := range providers { | ||
k, err := json.Marshal(p.Config()) | ||
if err != nil { | ||
log15.Error("Omitting auth provider, because could not JSON-marshal its config", "error", err, "configID", p.ConfigID()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
log15.Error("Omitting auth provider, because could not JSON-marshal its config", "error", err, "configID", p.ConfigID()) | |
log15.Error("Omitting auth provider (failed to marshal its JSON config)", "error", err, "configID", p.ConfigID()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this include the label of the provider? for improved debugging
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p.ConfigID()
identifies it
// curProviders is a map (label -> (config string -> Provider)). The first key is the label | ||
// under which the provider was registered. The second key is the normalized JSON serialization | ||
// of Provider.Config(). | ||
curProviders = map[string]map[string]Provider{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like this double nested map is too complex for what we are doing. I am slightly worried about the lack of types (using string
) too. Is there a way we could make it cleaner by using a struct or anything?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should rename label
to pkgName
where we use it
cmd/frontend/auth/providers.go
Outdated
for _, p := range providers { | ||
k, err := json.Marshal(p.Config()) | ||
if err != nil { | ||
log15.Error("Omitting auth provider, because could not JSON-marshal its config", "error", err, "configID", p.ConfigID()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this include the label of the provider? for improved debugging
// registered via UpdateProviders). | ||
func GetProviderByConfigID(id ProviderConfigID) Provider { | ||
var ps []Provider | ||
func Providers() []Provider { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add docs to Providers
and clarify what it returns when there are no providers?
} | ||
if !op { | ||
panic(fmt.Sprintf("UpdateProviders: provider to remove did not exist: %+v", p)) | ||
providers := make([]Provider, 0, ct) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: The double nested map curProviders
makes this hard to read. I thought lp
was the label name (human readable name) and not the submap of providers
Do we need the complexity here of calculating the array preallocation length? Why not just use something like 64
or even 128
? (these are all pointer allocations, 128*8 == 1024 bytes memory, so very cheap anyway)
aj := allProviders[j].ConfigID() | ||
return ai.Type < aj.Type || (ai.Type == aj.Type && ai.ID < aj.ID) | ||
}) | ||
sort.Sort(sortProviders(providers)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document why sorting these is important or not. If it's not important, why do we do it?
Do we need stable sorting here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the UI, will doc.
func (p sortProviders) Len() int { | ||
return len(p) | ||
} | ||
func (p sortProviders) Less(i, j int) bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This less func is hard to read (that is fine / expected) but the intent is not documented. If there is a bug here, it would be hard to catch without knowing the intent. If I read it correctly, the intent is: builtin
first, then by ConfigID().Type
, then by ConfigID().ID
cmd/frontend/auth/user.go
Outdated
if MockCreateOrUpdateUser != nil { | ||
userID, err = MockCreateOrUpdateUser(newOrUpdatedUser, externalAccount) | ||
return userID, "", err | ||
func GetAndSaveUser(ctx context.Context, op GetUserOp) (userID int32, safeErrMsg string, err error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As it stands this function returns two error message parameters that are easy to mix up by accident. A better approach would be to return a struct with all of these values, which means to access them you MUST acknowledge their names (result.UserID
, result.SafeErrMsg
, result.Err
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll follow up with this refactor in a later PR (want to get this deployed to dogfood for testing)
auth.UpdateProviders("builtin", nil) | ||
} else { | ||
auth.UpdateProviders("builtin", []auth.Provider{&provider{c: newPC}}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Return inside newPC == nil
conditional instead and remove else
?
if newPC == nil { | ||
auth.UpdateProviders("httpheader", nil) | ||
} else { | ||
auth.UpdateProviders("httpheader", []auth.Provider{&provider{c: newPC}}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
drop else / use early return here
} | ||
userID, userSaved, extAcctSaved, safeErrMsg, err := func() (int32, bool, bool, string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably makes sense to use a struct type here (e.g. declared inline). Otherwise it is hard to read + easy to mix up e.g. userSaved
and extAcctSaved
by accident
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM aside from my unresolved inline comments
@sqs nothing in particular, but you've "requested changes"—if everything looks fine, can you approve? |
…der instances); fixes panic when config cahnges
755b97e
to
7de3b20
Compare
Change summary:
auth.CreateOrUpdateUser
function to beauth.GetAndSaveUser
. This makes the code clearer and gives clear contracts on how the user identity is inferred when signing in via external auth mechanism. See the docstring for that function.allowSignup
field to the GitHub auth provider config. This is analogous to theallowSignup
field in the builtin auth provider. If set to true, users can sign up by authenticating via their GitHub account. If set to false, an existing account must exist that corresponds to the GitHub identity by verified email equivalency. Its default value is false (cc @sqs okay with you?)auth.UpdateProviders
method. Previously, there was a complicated (IMO) mechanism to ensure that existingProvider
instances were not deleted/re-created if their config had not actually changed (i.e., reuse the sameProvider
instance if possible). Now, it just re-creates allauth.Provider
instances on any config change. The added complexity didn't seem worth it to me, since none of theauth.Provider
impls maintain internal state that would cause issues if it were discarded. There was a bug in the previous code that caused a panic on config update and I wasn't able to pin down the issue after about an hour of debugging. cc @sqs I think you were the original author—was there any other reason I missed why we could recreate allauth.Providers
on every config refresh?This will allow us to enable GitHub auth on dogfood.