mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
2978 files. The fork is 2026-07-15 (rebrand: gitea.dev -> github.com/hanzoai/git), so 2026 is the year, and "Copyright 2026 Hanzo AI, Inc." was already the form used by the handful of files that had ours. Shape is now: // Copyright 2026 Hanzo AI, Inc. All rights reserved. // Copyright 2015 The Gogs Authors. All rights reserved. // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT Ours on top, theirs retained verbatim. Retained because MIT requires it, and separately because they are true — those authors did write that code. Not renamed to "The Git Authors": Git is a different project under a different license, and putting its name here would attribute this code to people who have nothing to do with it. Adding our line is the fix; overwriting theirs would have been both a licence violation and a false statement. gofmt still reports import-order drift in some of these files. It predates this change (verified against a stash) and is left alone rather than burying a one-line-per-file legal change under 3000 files of unrelated reordering. Build clean. Co-authored-by: Hanzo Dev <[email protected]>
108 lines
3.2 KiB
Go
108 lines
3.2 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package oauth2
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/hanzoai/git/models/auth"
|
|
"github.com/hanzoai/git/models/db"
|
|
user_model "github.com/hanzoai/git/models/user"
|
|
"github.com/hanzoai/git/modules/log"
|
|
|
|
"github.com/markbates/goth"
|
|
"golang.org/x/oauth2"
|
|
)
|
|
|
|
// Sync causes this OAuth2 source to synchronize its users with the db.
|
|
func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
|
|
log.Trace("Doing: SyncExternalUsers[%s] %d", source.AuthSource.Name, source.AuthSource.ID)
|
|
|
|
if !updateExisting {
|
|
log.Info("SyncExternalUsers[%s] not running since updateExisting is false", source.AuthSource.Name)
|
|
return nil
|
|
}
|
|
|
|
provider, err := createProvider(source.AuthSource.Name, source)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !provider.RefreshTokenAvailable() {
|
|
log.Trace("SyncExternalUsers[%s] provider doesn't support refresh tokens, can't synchronize", source.AuthSource.Name)
|
|
return nil
|
|
}
|
|
|
|
opts := user_model.FindExternalUserOptions{
|
|
HasRefreshToken: true,
|
|
Expired: true,
|
|
LoginSourceID: source.AuthSource.ID,
|
|
}
|
|
|
|
return user_model.IterateExternalLogin(ctx, opts, func(ctx context.Context, u *user_model.ExternalLoginUser) error {
|
|
return source.refresh(ctx, provider, u)
|
|
})
|
|
}
|
|
|
|
func (source *Source) refresh(ctx context.Context, provider goth.Provider, u *user_model.ExternalLoginUser) error {
|
|
log.Trace("Syncing login_source_id=%d external_id=%s expiration=%s", u.LoginSourceID, u.ExternalID, u.ExpiresAt)
|
|
|
|
shouldDisable := false
|
|
|
|
token, err := provider.RefreshToken(u.RefreshToken)
|
|
if err != nil {
|
|
if err, ok := err.(*oauth2.RetrieveError); ok && err.ErrorCode == "invalid_grant" {
|
|
// this signals that the token is not valid and the user should be disabled
|
|
shouldDisable = true
|
|
} else {
|
|
return err
|
|
}
|
|
}
|
|
|
|
user, hasUser, err := user_model.GetIndividualUserByLoginSource(ctx, auth.OAuth2, u.LoginSourceID, u.ExternalID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// If the grant is no longer valid, disable the user and
|
|
// delete local tokens. If the OAuth2 provider still
|
|
// recognizes them as a valid user, they will be able to login
|
|
// via their provider and reactivate their account.
|
|
if shouldDisable {
|
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
|
if hasUser {
|
|
log.Info("SyncExternalUsers[%s] disabling user %d", source.AuthSource.Name, user.ID)
|
|
user.IsActive = false
|
|
if err := user_model.UpdateUserCols(ctx, user, "is_active"); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// HINT: OAUTH-AUTO-SYNC-USER-ACTIVATION
|
|
// Delete stored tokens, since they are invalid. This also prevents us from checking this in subsequent runs.
|
|
u.AccessToken = ""
|
|
u.RefreshToken = ""
|
|
u.ExpiresAt = time.Time{}
|
|
|
|
return user_model.UpdateExternalUserByExternalID(ctx, u)
|
|
})
|
|
}
|
|
|
|
// Otherwise, update the tokens
|
|
u.AccessToken = token.AccessToken
|
|
u.ExpiresAt = token.Expiry
|
|
|
|
// Some providers only update access tokens provide a new
|
|
// refresh token, so avoid updating it if it's empty
|
|
if token.RefreshToken != "" {
|
|
u.RefreshToken = token.RefreshToken
|
|
}
|
|
|
|
err = user_model.UpdateExternalUserByExternalID(ctx, u)
|
|
|
|
return err
|
|
}
|