diff --git a/handlers.go b/handlers.go index b904b68..4509b7f 100644 --- a/handlers.go +++ b/handlers.go @@ -87,7 +87,7 @@ func (f *faucet) authOpenHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext if err != nil { return err } - if funded, t := f.storage.checkIsFundedAddress(addr, "open"); funded { + if funded, t := f.storage.checkIsFundedUserID(addr.Bytes(), "open"); funded { errReason := fmt.Sprintf("address %s already funded, wait until %s", addr.Hex(), t) return ctx.Send(new(HandlerResponse).SetError(errReason).MustMarshall(), CodeErrFlood) } @@ -95,7 +95,7 @@ func (f *faucet) authOpenHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext if err != nil { return err } - if err := f.storage.addFundedAddress(addr, "open"); err != nil { + if err := f.storage.addFundedUserID(addr.Bytes(), "open"); err != nil { return err } return ctx.Send(new(HandlerResponse).Set(data).MustMarshall(), apirest.HTTPstatusOK) @@ -123,7 +123,7 @@ func (f *faucet) authOAuthHandler(msg *apirest.APIdata, ctx *httprouter.HTTPCont if err != nil { return err } - if funded, t := f.storage.checkIsFundedAddress(addr, "oauth"); funded { + if funded, t := f.storage.checkIsFundedUserID(addr.Bytes(), "oauth"); funded { errReason := fmt.Sprintf("address %s already funded, wait until %s", addr.Hex(), t) return ctx.Send(new(HandlerResponse).SetError(errReason).MustMarshall(), CodeErrFlood) } @@ -139,16 +139,41 @@ func (f *faucet) authOAuthHandler(msg *apirest.APIdata, ctx *httprouter.HTTPCont return ctx.Send(new(HandlerResponse).SetError(ReasonErrOauthProviderNotFound).MustMarshall(), CodeErrOauthProviderNotFound) } - _, err = provider.GetOAuthToken(newRequest.Code, newRequest.RedirectURL) + token, err := provider.GetOAuthToken(newRequest.Code, newRequest.RedirectURL) if err != nil { return ctx.Send(new(HandlerResponse).SetError(ReasonErrOauthProviderError).MustMarshall(), CodeErrOauthProviderError) } + profileRaw, err := provider.GetOAuthProfile(token) + if err != nil { + log.Warnw("error obtaining the profile", "err", err) + return ctx.Send(new(HandlerResponse).SetError(ReasonErrOauthProviderError).MustMarshall(), CodeErrOauthProviderError) + } + + var profile map[string]interface{} + if err := json.Unmarshal(profileRaw, &profile); err != nil { + log.Warnw("error marshalling the profile", "err", err) + return ctx.Send(new(HandlerResponse).SetError(ReasonErrOauthProviderError).MustMarshall(), CodeErrOauthProviderError) + } + + // Check if the oauth profile is already funded + fundedProfileField := profile[provider.UsernameField].(string) + fundedAuthType := "oauth_" + newRequest.Provider + if funded, t := f.storage.checkIsFundedUserID([]byte(fundedProfileField), fundedAuthType); funded { + errReason := fmt.Sprintf("user %s already funded, wait until %s", fundedProfileField, t) + return ctx.Send(new(HandlerResponse).SetError(errReason).MustMarshall(), CodeErrFlood) + } + data, err := f.prepareFaucetPackage(addr, "oauth") if err != nil { return err } - if err := f.storage.addFundedAddress(addr, "oauth"); err != nil { + + // Add address and profile to the funded list + if err := f.storage.addFundedUserID(addr.Bytes(), "oauth"); err != nil { + return err + } + if err := f.storage.addFundedUserID([]byte(fundedProfileField), fundedAuthType); err != nil { return err } @@ -209,7 +234,7 @@ func (f *faucet) authAragonDaoHandler(msg *apirest.APIdata, ctx *httprouter.HTTP } // Check if the address is already funded - if funded, t := f.storage.checkIsFundedAddress(addr, "aragon"); funded { + if funded, t := f.storage.checkIsFundedUserID(addr.Bytes(), "aragon"); funded { errReason := fmt.Sprintf("address %s already funded, wait until %s", addr.Hex(), t) return ctx.Send(new(HandlerResponse).SetError(errReason).MustMarshall(), CodeErrFlood) } @@ -237,7 +262,7 @@ func (f *faucet) authAragonDaoHandler(msg *apirest.APIdata, ctx *httprouter.HTTP return ctx.Send(new(HandlerResponse).SetError(err.Error()).MustMarshall(), CodeErrInternalError) } - if err := f.storage.addFundedAddress(addr, "aragon"); err != nil { + if err := f.storage.addFundedUserID(addr.Bytes(), "aragon"); err != nil { return ctx.Send(new(HandlerResponse).SetError(err.Error()).MustMarshall(), CodeErrInternalError) } diff --git a/oauthhandler/config.yml b/oauthhandler/config.yml index c6be7d3..eb06731 100644 --- a/oauthhandler/config.yml +++ b/oauthhandler/config.yml @@ -7,6 +7,7 @@ providers: client_id: FACEBOOK_CLIENT_ID client_secret: FACEBOOK_CLIENT_SECRET scope: email + username_field: email github: name: Github auth_url: https://github.com/login/oauth/authorize @@ -15,6 +16,7 @@ providers: client_id: GITHUB_CLIENT_ID client_secret: GITHUB_CLIENT_SECRET scope: user:email + username_field: login twitter: name: Twitter auth_url: https://api.twitter.com/oauth/authenticate @@ -46,4 +48,5 @@ providers: profile_url: https://www.googleapis.com/oauth2/v1/userinfo client_id: GOOGLE_CLIENT_ID client_secret: GOOGLE_CLIENT_SECRET - scope: email \ No newline at end of file + scope: email + username_field: id \ No newline at end of file diff --git a/oauthhandler/providers.go b/oauthhandler/providers.go index b50fb85..1a5f560 100644 --- a/oauthhandler/providers.go +++ b/oauthhandler/providers.go @@ -22,24 +22,26 @@ type Config struct { // ProviderConfig represents the configuration for an OAuth provider. type ProviderConfig struct { - Name string `yaml:"name"` - AuthURL string `yaml:"auth_url"` - TokenURL string `yaml:"token_url"` - ProfileURL string `yaml:"profile_url"` - ClientID string `yaml:"client_id"` - ClientSecret string `yaml:"client_secret"` - Scope string `yaml:"scope"` + Name string `yaml:"name"` + AuthURL string `yaml:"auth_url"` + TokenURL string `yaml:"token_url"` + ProfileURL string `yaml:"profile_url"` + ClientID string `yaml:"client_id"` + ClientSecret string `yaml:"client_secret"` + Scope string `yaml:"scope"` + UsernameField string `yaml:"username_field"` } // Provider is the OAuth provider. type Provider struct { - Name string - AuthURL string - TokenURL string - ProfileURL string - ClientID string - ClientSecret string - Scope string + Name string + AuthURL string + TokenURL string + ProfileURL string + ClientID string + ClientSecret string + Scope string + UsernameField string } // OAuthToken is the OAuth token. @@ -50,15 +52,16 @@ type OAuthToken struct { } // NewProvider creates a new OAuth provider. -func NewProvider(name, authURL, tokenURL, profileURL, clientID, clientSecret, scope string) *Provider { +func NewProvider(name, authURL, tokenURL, profileURL, clientID, clientSecret, scope string, usernameField string) *Provider { return &Provider{ - Name: name, - AuthURL: authURL, - TokenURL: tokenURL, - ProfileURL: profileURL, - ClientID: clientID, - ClientSecret: clientSecret, - Scope: scope, + Name: name, + AuthURL: authURL, + TokenURL: tokenURL, + ProfileURL: profileURL, + ClientID: clientID, + ClientSecret: clientSecret, + Scope: scope, + UsernameField: usernameField, } } @@ -92,6 +95,7 @@ func InitProviders() (map[string]*Provider, error) { viper.GetString(conf.ClientID), viper.GetString(conf.ClientSecret), conf.Scope, + conf.UsernameField, ) providers[name] = provider } diff --git a/storage.go b/storage.go index 85bfcdf..1c91685 100644 --- a/storage.go +++ b/storage.go @@ -7,17 +7,12 @@ import ( "path/filepath" "time" - "github.com/ethereum/go-ethereum/common" "go.vocdoni.io/dvote/db" "go.vocdoni.io/dvote/db/metadb" "go.vocdoni.io/dvote/db/prefixeddb" "go.vocdoni.io/dvote/log" ) -const ( - fundedAddressPrefix = "a_" -) - type storage struct { kv db.Database waitPeriodSeconds uint64 @@ -40,12 +35,12 @@ func newStorage(dbType string, dataDir string, waitPeriod time.Duration, dbPrefi return st, nil } -// addFundedAddress adds the given address to the funded addresses list, with the current time +// addFundedUserID adds the given userID to the funded list, with the current time // as the wait period end time. -func (st *storage) addFundedAddress(addr common.Address, authType string) error { +func (st *storage) addFundedUserID(userID []byte, authType string) error { tx := st.kv.WriteTx() defer tx.Discard() - key := append([]byte(fundedAddressPrefix), append(addr.Bytes(), []byte(authType)...)...) + key := append(userID, []byte(authType)...) wp := uint64(time.Now().Unix()) + st.waitPeriodSeconds wpBytes := make([]byte, 8) binary.LittleEndian.PutUint64(wpBytes, wp) @@ -55,11 +50,10 @@ func (st *storage) addFundedAddress(addr common.Address, authType string) error return tx.Commit() } -// checkIsFundedAddress checks if the given address is funded and returns true if it is, within +// checkIsFundedUserID checks if the given text is funded and returns true if it is, within // the wait period time window. Otherwise, it returns false. -// The second return value is the wait period end time, if the address is funded. -func (st *storage) checkIsFundedAddress(addr common.Address, authType string) (bool, time.Time) { - key := append([]byte(fundedAddressPrefix), append(addr.Bytes(), []byte(authType)...)...) +func (st *storage) checkIsFundedUserID(userID []byte, authType string) (bool, time.Time) { + key := append(userID, []byte(authType)...) wpBytes, err := st.kv.Get(key) if err != nil { return false, time.Time{}