aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpg9182 <96569817+pg9182@users.noreply.github.com>2023-02-27 00:28:35 -0500
committerpg9182 <96569817+pg9182@users.noreply.github.com>2023-03-04 00:49:32 -0500
commite662e1297b2d5b6b46549788171a79eedf4caca7 (patch)
tree806a65637820f5784b039eb4de10cf91008a343c
parentd905e4eca49f121dbd374254daa6b506d102045e (diff)
downloadAtlas-e662e1297b2d5b6b46549788171a79eedf4caca7.tar.gz
Atlas-e662e1297b2d5b6b46549788171a79eedf4caca7.zip
pkg/atlas, pkg/api/api0: Make username source configurable
-rw-r--r--pkg/api/api0/api.go7
-rw-r--r--pkg/api/api0/client.go141
-rw-r--r--pkg/atlas/config.go12
-rw-r--r--pkg/atlas/server.go22
4 files changed, 126 insertions, 56 deletions
diff --git a/pkg/api/api0/api.go b/pkg/api/api0/api.go
index 1c2683f..3ed5999 100644
--- a/pkg/api/api0/api.go
+++ b/pkg/api/api0/api.go
@@ -43,8 +43,11 @@ type Handler struct {
// PdataStorage stores player data. It must be non-nil.
PdataStorage PdataStorage
- // OriginAuthMgr manages Origin nucleus tokens (used for checking
- // usernames). If not provided, usernames will not be updated.
+ // UsernameSource configures the source to use for usernames.
+ UsernameSource UsernameSource
+
+ // OriginAuthMgr, if provided, manages Origin nucleus tokens for checking
+ // usernames.
OriginAuthMgr *origin.AuthMgr
// CleanBadWords is used to filter bad words from server names and
diff --git a/pkg/api/api0/client.go b/pkg/api/api0/client.go
index b64659a..9b1408f 100644
--- a/pkg/api/api0/client.go
+++ b/pkg/api/api0/client.go
@@ -18,6 +18,17 @@ import (
"github.com/rs/zerolog/hlog"
)
+// UsernameSource determines where to get player in-game usernames from.
+type UsernameSource string
+
+const (
+ // Don't get usernames.
+ UsernameSourceNone UsernameSource = ""
+
+ // Get the username from the Origin API.
+ UsernameSourceOrigin UsernameSource = "origin"
+)
+
type MainMenuPromos struct {
NewInfo MainMenuPromosNew `json:"newInfo"`
LargeButton MainMenuPromosButtonLarge `json:"largeButton"`
@@ -186,56 +197,7 @@ func (h *Handler) handleClientOriginAuth(w http.ResponseWriter, r *http.Request)
}
}
- var username string
- if h.OriginAuthMgr != nil {
- originStart := time.Now()
- if tok, ours, err := h.OriginAuthMgr.OriginAuth(false); err == nil {
- var notfound bool
- if ui, err := origin.GetUserInfo(r.Context(), tok, uid); err == nil {
- if len(ui) == 1 {
- username = ui[0].EAID
- h.m().client_originauth_origin_username_lookup_calls_total.success.Inc()
- } else {
- notfound = true
- h.m().client_originauth_origin_username_lookup_calls_total.notfound.Inc()
- }
- } else if errors.Is(err, origin.ErrAuthRequired) {
- if tok, ours, err := h.OriginAuthMgr.OriginAuth(true); err == nil {
- if ui, err := origin.GetUserInfo(r.Context(), tok, uid); err == nil {
- if len(ui) == 1 {
- username = ui[0].EAID
- h.m().client_originauth_origin_username_lookup_calls_total.success.Inc()
- } else {
- notfound = true
- h.m().client_originauth_origin_username_lookup_calls_total.notfound.Inc()
- }
- }
- } else if ours {
- hlog.FromRequest(r).Error().
- Err(err).
- Msgf("origin auth token refresh failure")
- h.m().client_originauth_origin_username_lookup_calls_total.fail_authtok_refresh.Inc()
- }
- } else if !errors.Is(err, context.Canceled) {
- hlog.FromRequest(r).Error().
- Err(err).
- Msgf("failed to get origin user info")
- h.m().client_originauth_origin_username_lookup_calls_total.fail_other_error.Inc()
- }
- if notfound {
- hlog.FromRequest(r).Warn().
- Err(err).
- Uint64("uid", uid).
- Msgf("no username found for uid")
- }
- } else if ours {
- hlog.FromRequest(r).Error().
- Err(err).
- Msgf("origin auth token refresh failure")
- h.m().client_originauth_origin_username_lookup_calls_total.fail_authtok_refresh.Inc()
- }
- h.m().client_originauth_origin_username_lookup_duration_seconds.UpdateDuration(originStart)
- }
+ username := h.lookupUsername(r, uid)
// note: there's small chance of race conditions here if there are multiple
// concurrent origin_auth calls, but since we only ever support one session
@@ -258,7 +220,7 @@ func (h *Handler) handleClientOriginAuth(w http.ResponseWriter, r *http.Request)
}
if acct != nil && username != "" && acct.Username != username {
- hlog.FromRequest(r).Info().Uint64("uid", acct.UID).Str("username", username).Str("prev_username", acct.Username).Msg("got updated username from origin")
+ hlog.FromRequest(r).Info().Uint64("uid", acct.UID).Str("username", username).Str("prev_username", acct.Username).Msg("got updated username")
}
if acct == nil {
acct = &Account{
@@ -306,6 +268,83 @@ func (h *Handler) handleClientOriginAuth(w http.ResponseWriter, r *http.Request)
})
}
+// lookupUsername gets the username for uid according to the configured
+// UsernameSource, returning an empty string if not found or on error.
+func (h *Handler) lookupUsername(r *http.Request, uid uint64) (username string) {
+ switch h.UsernameSource {
+ case UsernameSourceNone:
+ break
+ case UsernameSourceOrigin:
+ username, _ = h.lookupUsernameOrigin(r, uid)
+ default:
+ hlog.FromRequest(r).Error().
+ Msgf("unknown username source %q", h.UsernameSource)
+ }
+ return
+}
+
+// lookupUsernameOrigin gets the username for uid from the Origin API, returning
+// an empty string if a username does not exist for the uid, and false on error.
+func (h *Handler) lookupUsernameOrigin(r *http.Request, uid uint64) (username string, ok bool) {
+ if h.OriginAuthMgr == nil {
+ hlog.FromRequest(r).Error().
+ Str("username_source", "origin").
+ Msgf("no origin auth available for username lookup")
+ return
+ }
+ originStart := time.Now()
+ if tok, ours, err := h.OriginAuthMgr.OriginAuth(false); err == nil {
+ if ui, err := origin.GetUserInfo(r.Context(), tok, uid); err == nil {
+ if len(ui) == 1 {
+ username = ui[0].EAID
+ h.m().client_originauth_origin_username_lookup_calls_total.success.Inc()
+ } else {
+ h.m().client_originauth_origin_username_lookup_calls_total.notfound.Inc()
+ }
+ ok = true
+ } else if errors.Is(err, origin.ErrAuthRequired) {
+ if tok, ours, err := h.OriginAuthMgr.OriginAuth(true); err == nil {
+ if ui, err := origin.GetUserInfo(r.Context(), tok, uid); err == nil {
+ if len(ui) == 1 {
+ username = ui[0].EAID
+ h.m().client_originauth_origin_username_lookup_calls_total.success.Inc()
+ } else {
+ h.m().client_originauth_origin_username_lookup_calls_total.notfound.Inc()
+ }
+ ok = true
+ }
+ } else if ours {
+ hlog.FromRequest(r).Error().
+ Err(err).
+ Str("username_source", "origin").
+ Msgf("origin auth token refresh failure")
+ h.m().client_originauth_origin_username_lookup_calls_total.fail_authtok_refresh.Inc()
+ }
+ } else if !errors.Is(err, context.Canceled) {
+ hlog.FromRequest(r).Error().
+ Err(err).
+ Str("username_source", "origin").
+ Msgf("failed to get origin user info")
+ h.m().client_originauth_origin_username_lookup_calls_total.fail_other_error.Inc()
+ }
+ if username == "" && ok {
+ hlog.FromRequest(r).Warn().
+ Err(err).
+ Uint64("uid", uid).
+ Str("username_source", "origin").
+ Msgf("no origin username found for uid")
+ }
+ } else if ours {
+ hlog.FromRequest(r).Error().
+ Err(err).
+ Str("username_source", "origin").
+ Msgf("origin auth token refresh failure")
+ h.m().client_originauth_origin_username_lookup_calls_total.fail_authtok_refresh.Inc()
+ }
+ h.m().client_originauth_origin_username_lookup_duration_seconds.UpdateDuration(originStart)
+ return
+}
+
func (h *Handler) handleClientAuthWithServer(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodOptions && r.Method != http.MethodPost {
h.m().client_authwithserver_requests_total.http_method_not_allowed.Inc()
diff --git a/pkg/atlas/config.go b/pkg/atlas/config.go
index d519094..2829fce 100644
--- a/pkg/atlas/config.go
+++ b/pkg/atlas/config.go
@@ -160,9 +160,15 @@ type Config struct {
// API0_MinimumLauncherVersion.
API0_MainMenuPromos_UpdateNeeded string `env:"ATLAS_API0_MAINMENUPROMOS_UPDATENEEDED=none"`
- // The email address to use for Origin login. If not provided, usernames are
- // not resolved during authentication. If it begins with @, it is treated
- // as the name of a systemd credential to load.
+ // Sets the source used for resolving usernames. If not specified, "origin"
+ // is used if OriginEmail is provided, otherwise, "none" is used.
+ // - none (don't get usernames)
+ // - origin (get the username from the Origin API)
+ UsernameSource string `env:"ATLAS_USERNAMESOURCE"`
+
+ // The email address to use for Origin login. If not provided, the Origin
+ // API will not be used. If it begins with @, it is treated as the name of a
+ // systemd credential to load.
OriginEmail string `env:"ATLAS_ORIGIN_EMAIL" sdcreds:"load,trimspace"`
// The password for Origin login. If it begins with @, it is treated as the
diff --git a/pkg/atlas/server.go b/pkg/atlas/server.go
index 145bb45..40d2abf 100644
--- a/pkg/atlas/server.go
+++ b/pkg/atlas/server.go
@@ -299,6 +299,11 @@ func NewServer(c *Config) (*Server, error) {
} else {
return nil, fmt.Errorf("initialize origin auth: %w", err)
}
+ if x, err := configureUsernameSource(c); err == nil {
+ s.API0.UsernameSource = x
+ } else {
+ return nil, fmt.Errorf("initialize username lookup: %w", err)
+ }
if astore, err := configureAccountStorage(c); err == nil {
s.API0.AccountStorage = astore
} else {
@@ -610,6 +615,23 @@ func configureOrigin(c *Config, l zerolog.Logger) (*origin.AuthMgr, error) {
return mgr, nil
}
+func configureUsernameSource(c *Config) (api0.UsernameSource, error) {
+ switch typ := c.UsernameSource; typ {
+ case "none":
+ return api0.UsernameSourceNone, nil
+ case "origin":
+ return api0.UsernameSourceOrigin, nil
+ case "":
+ // backwards compat
+ if c.OriginEmail != "" {
+ return api0.UsernameSourceOrigin, nil
+ }
+ return api0.UsernameSourceNone, nil
+ default:
+ return "", fmt.Errorf("unknown source %q", typ)
+ }
+}
+
func configureAccountStorage(c *Config) (api0.AccountStorage, error) {
switch typ, arg, _ := strings.Cut(c.API0_Storage_Accounts, ":"); typ {
case "memory":