nidus-sync/platform/oauth.go
Eli Ribble 44c4f17f32
Massive rework of platform layer user/organization
The goal of this rework is to make it so I can pass around platform.User
instead of a pair of models.Organization and models.User. This is useful
for reason I kind of forget now, but it started with working on
notifications and ballooned massively from there into refactoring a
number of things that were bugging me.

This also includes a tiny amount of work on server-side events (SSE).

 * background stuff lives inside the platform now, which I need for
   having it push updates through SSE
 * userfile now lives in the platform, under file, so other platform
   functions can safely use it
 * oauth is broken into pieces and inside platform because other stuff
   was calling it already, but badly.
 * notifications go into the platform as well
2026-03-12 23:49:16 +00:00

77 lines
2.5 KiB
Go

package platform
import (
"context"
"fmt"
"net/url"
"time"
"github.com/Gleipnir-Technology/bob/dialect/psql/sm"
"github.com/Gleipnir-Technology/nidus-sync/config"
"github.com/Gleipnir-Technology/nidus-sync/db"
"github.com/Gleipnir-Technology/nidus-sync/db/models"
"github.com/Gleipnir-Technology/nidus-sync/platform/background"
"github.com/Gleipnir-Technology/nidus-sync/platform/oauth"
"github.com/aarondl/opt/omit"
"github.com/aarondl/opt/omitnull"
)
// When there is no oauth for an organization
type NoOAuthForOrg struct{}
func (e NoOAuthForOrg) Error() string { return "No oauth available for organization" }
func GetOAuthForOrg(ctx context.Context, org Organization) (*models.ArcgisOauthToken, error) {
result, err := oauth.GetOAuthForOrg(ctx, org.model)
if result == nil && err == nil {
return nil, &NoOAuthForOrg{}
}
return result, err
}
func GetOAuthForUser(ctx context.Context, user User) (*models.ArcgisOauthToken, error) {
oauth, err := user.model.UserOauthTokens(
sm.OrderBy("created").Desc(),
).One(ctx, db.PGInstance.BobDB)
if err != nil {
if err.Error() == "sql: no rows in result set" {
return nil, nil
}
return nil, err
}
return oauth, nil
}
func HandleOauthAccessCode(ctx context.Context, user User, code string) error {
form := url.Values{
"grant_type": []string{"authorization_code"},
"code": []string{code},
"redirect_uri": []string{config.ArcGISOauthRedirectURL()},
}
token, err := oauth.DoTokenRequest(ctx, form)
if err != nil {
return fmt.Errorf("Failed to exchange authorization code for token: %w", err)
}
accessExpires := oauth.FutureUTCTimestamp(token.ExpiresIn)
refreshExpires := oauth.FutureUTCTimestamp(token.RefreshTokenExpiresIn)
setter := models.ArcgisOauthTokenSetter{
AccessToken: omit.From(token.AccessToken),
AccessTokenExpires: omit.From(accessExpires),
//ArcgisAccountID: omit.From(
ArcgisID: omitnull.FromPtr[string](nil),
ArcgisLicenseTypeID: omitnull.FromPtr[string](nil),
Created: omit.From(time.Now()),
InvalidatedAt: omitnull.FromPtr[time.Time](nil),
RefreshToken: omit.From(token.RefreshToken),
RefreshTokenExpires: omit.From(refreshExpires),
UserID: omit.From(int32(user.ID)),
Username: omit.From(token.Username),
}
oauth, err := models.ArcgisOauthTokens.Insert(&setter).One(ctx, db.PGInstance.BobDB)
if err != nil {
return fmt.Errorf("Failed to save token to database: %w", err)
}
go background.UpdateArcgisUserData(context.Background(), user.model, oauth)
return nil
}