Skip to content

Commit

Permalink
made social kyc more dynamic and reactive to kyc cfg changes
Browse files Browse the repository at this point in the history
  • Loading branch information
ice-ares committed Jul 9, 2024
1 parent fb914dc commit f0d0e4d
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 35 deletions.
37 changes: 21 additions & 16 deletions kyc/social/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,29 +121,34 @@ type (

kycConfigJSON struct {
Social1KYC struct {
XPostLink string `json:"xPostLink"`
xPostPatternTemplate *template.Template `json:"-"` //nolint:revive // .
XPostPattern string `json:"xPostPattern"`
XPostLink string `json:"xPostLink"`
} `json:"social1-kyc"` //nolint:tagliatelle // .
Social2KYC struct {
XPostLink string `json:"xPostLink"`
xPostPatternTemplate *template.Template `json:"-"` //nolint:revive // .
XPostPattern string `json:"xPostPattern"`
XPostLink string `json:"xPostLink"`
} `json:"social2-kyc"` //nolint:tagliatelle // .
DynamicDistributionSocialKYC []*struct {
XPostLink string `json:"xPostLink"`
KYCStep users.KYCStep `json:"step"` //nolint:tagliatelle // .
xPostPatternTemplate *template.Template `json:"-"` //nolint:revive // .
XPostPattern string `json:"xPostPattern"`
XPostLink string `json:"xPostLink"`
KYCStep users.KYCStep `json:"step"` //nolint:tagliatelle // .
} `json:"dynamic-distribution-kyc"` //nolint:tagliatelle // .
}

config struct {
alertFrequency *sync.Map // .map[users.KYCStep]stdlibtime.Duration.
kycConfigJSON *atomic.Pointer[kycConfigJSON]
ConfigJSONURL string `yaml:"config-json-url" mapstructure:"config-json-url"` //nolint:tagliatelle // .
Environment string `yaml:"environment" mapstructure:"environment"`
AlertSlackWebhook string `yaml:"alert-slack-webhook" mapstructure:"alert-slack-webhook"` //nolint:tagliatelle // .
TenantName string `yaml:"tenant-name" mapstructure:"tenant-name"` //nolint:tagliatelle // .
ReferralInviteURLPrefix string `yaml:"referralInviteUrlPrefix" mapstructure:"referralInviteUrlPrefix"` //nolint:tagliatelle // .
DelayBetweenSessions stdlibtime.Duration `yaml:"delay-between-sessions" mapstructure:"delay-between-sessions"` //nolint:tagliatelle // .
SessionWindow stdlibtime.Duration `yaml:"session-window" mapstructure:"session-window"` //nolint:tagliatelle // .
MaxSessionsAllowed int `yaml:"max-sessions-allowed" mapstructure:"max-sessions-allowed"` //nolint:tagliatelle // .
MaxAttemptsAllowed uint8 `yaml:"max-attempts-allowed" mapstructure:"max-attempts-allowed"` //nolint:tagliatelle // .
EnableAlerts bool `yaml:"enable-alerts" mapstructure:"enable-alerts"` //nolint:tagliatelle // .
alertFrequency *sync.Map // .map[users.KYCStep]stdlibtime.Duration.
kycConfigJSON *atomic.Pointer[kycConfigJSON]
ConfigJSONURL string `yaml:"config-json-url" mapstructure:"config-json-url"` //nolint:tagliatelle // .
Environment string `yaml:"environment" mapstructure:"environment"`
AlertSlackWebhook string `yaml:"alert-slack-webhook" mapstructure:"alert-slack-webhook"` //nolint:tagliatelle // .
TenantName string `yaml:"tenant-name" mapstructure:"tenant-name"` //nolint:tagliatelle // .
DelayBetweenSessions stdlibtime.Duration `yaml:"delay-between-sessions" mapstructure:"delay-between-sessions"` //nolint:tagliatelle // .
SessionWindow stdlibtime.Duration `yaml:"session-window" mapstructure:"session-window"` //nolint:tagliatelle // .
MaxSessionsAllowed int `yaml:"max-sessions-allowed" mapstructure:"max-sessions-allowed"` //nolint:tagliatelle // .
MaxAttemptsAllowed uint8 `yaml:"max-attempts-allowed" mapstructure:"max-attempts-allowed"` //nolint:tagliatelle // .
EnableAlerts bool `yaml:"enable-alerts" mapstructure:"enable-alerts"` //nolint:tagliatelle // .
}
)
34 changes: 32 additions & 2 deletions kyc/social/remote_kyc_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ package social

import (
"context"
"fmt"
"net/http"
"strings"
"sync/atomic"
"text/template"
stdlibtime "time"

"github.com/goccy/go-json"
Expand Down Expand Up @@ -40,7 +42,7 @@ func (r *repository) startKYCConfigJSONSyncer(ctx context.Context) {
}
}

//nolint:funlen,gomnd // .
//nolint:funlen,gomnd,nestif // .
func (r *repository) syncKYCConfigJSON(ctx context.Context) error {
if resp, err := req.
SetContext(ctx).
Expand Down Expand Up @@ -69,11 +71,39 @@ func (r *repository) syncKYCConfigJSON(ctx context.Context) error {
if err = json.UnmarshalContext(ctx, data, &kycConfig); err != nil {
return errors.Wrapf(err, "failed to unmarshal into %#v, data: %v", kycConfig, string(data))
}
if body := string(data); !strings.Contains(body, "face-auth") && !strings.Contains(body, "web-face-auth") {
if body := string(data); !strings.Contains(body, "social1-kyc") && !strings.Contains(body, "social2-kyc") {
return errors.Errorf("there's something wrong with the KYCConfigJSON body: %v", body)
}
if err = r.initKYCConfig(&kycConfig); err != nil {
return errors.Wrapf(err, "failed to init KYCConfigJSON %+v", &kycConfig)
}
r.cfg.kycConfigJSON.Swap(&kycConfig)

return nil
}
}

func (*repository) initKYCConfig(kycCfg *kycConfigJSON) (err error) {
if pattern := kycCfg.Social1KYC.XPostPattern; pattern != "" {
kycCfg.Social1KYC.xPostPatternTemplate, err = template.New("kycCfg.Social1KYC.XPostPattern").Parse(pattern)
if err != nil {
return errors.Wrapf(err, "failed to parse kycCfg.Social1KYC.xPostPatternTemplate `%v`", pattern)
}
}
if pattern := kycCfg.Social2KYC.XPostPattern; pattern != "" {
kycCfg.Social2KYC.xPostPatternTemplate, err = template.New("kycCfg.Social2KYC.XPostPattern").Parse(pattern)
if err != nil {
return errors.Wrapf(err, "failed to parse kycCfg.Social2KYC.xPostPatternTemplate `%v`", pattern)
}
}
for ix, dynKYCCfg := range kycCfg.DynamicDistributionSocialKYC {
if pattern := dynKYCCfg.XPostPattern; pattern != "" {
dynKYCCfg.xPostPatternTemplate, err = template.New(fmt.Sprintf("dynKYCCfg%v.XPostPattern", ix)).Parse(pattern)
if err != nil {
return errors.Wrapf(err, "failed to parse dynKYCCfg%v.XPostPattern `%v`", ix, pattern)
}
}
}

return nil
}
52 changes: 35 additions & 17 deletions kyc/social/social.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,27 +185,14 @@ func (r *repository) VerifyPost(ctx context.Context, metadata *VerificationMetad
return nil, ErrNotAvailable
}
if metadata.Twitter.TweetURL == "" && metadata.Facebook.AccessToken == "" {
if true { // Because we want to be less strict, for the moment.
return &Verification{ExpectedPostText: fmt.Sprintf("%v%v", r.cfg.ReferralInviteURLPrefix, user.Username)}, nil
}

return &Verification{ExpectedPostText: metadata.expectedPostText(user.User, tenantName(r.cfg.TenantName))}, nil
return &Verification{ExpectedPostText: r.expectedPostText(user.User, metadata)}, nil
}
pvm := &social.Metadata{
AccessToken: metadata.Facebook.AccessToken,
PostURL: metadata.Twitter.TweetURL,
ExpectedPostText: metadata.expectedPostText(user.User, tenantName(r.cfg.TenantName)),
ExpectedPostText: r.expectedPostSubtext(user.User, metadata),
ExpectedPostURL: r.expectedPostURL(metadata),
}
if true { // Because we want to be less strict, for the moment.
pvm.ExpectedPostText = fmt.Sprintf("%v%v", r.cfg.ReferralInviteURLPrefix, user.Username)
pvm.ExpectedPostURL = ""
}
if metadata.Language == "zzzzzzzzzz" { // This is for testing purposes.
stdlibtime.Sleep(120 * stdlibtime.Second) //nolint:gomnd // .
} else if metadata.Language == "yyyyyyyyyy" {
stdlibtime.Sleep(90 * stdlibtime.Second) //nolint:gomnd // .
}
userHandle, err := r.socialVerifiers[metadata.Social].VerifyPost(ctx, pvm)
if err != nil { //nolint:nestif // .
log.Error(errors.Wrapf(err, "social verification failed for KYCStep:%v,Social:%v,Language:%v,userID:%v",
Expand Down Expand Up @@ -395,8 +382,11 @@ func detectReason(err error) string {
}
}

func (vm *VerificationMetadata) expectedPostText(user *users.User, tname tenantName) string {
var templ *languageTemplate
func (r *repository) expectedPostText(user *users.User, vm *VerificationMetadata) string {
var (
templ *languageTemplate
tname = tenantName(r.cfg.TenantName)
)
if _, found := allTemplates[tname][vm.KYCStep][vm.Social][postContentLanguageTemplateType][vm.Language]; found {
randVal := getRandomIndex(int64(len(allTemplates[tname][vm.KYCStep][vm.Social][postContentLanguageTemplateType][vm.Language])))
templ = allTemplates[tname][vm.KYCStep][vm.Social][postContentLanguageTemplateType][vm.Language][randVal]
Expand All @@ -423,6 +413,34 @@ func getRandomIndex(maxVal int64) uint64 {
return n.Uint64()
}

func (r *repository) expectedPostSubtext(user *users.User, metadata *VerificationMetadata) string {
if metadata.Social == TwitterType {
var tmpl *template.Template
switch metadata.KYCStep { //nolint:exhaustive // Not needed. Everything else is validated before this.
case users.Social1KYCStep:
tmpl = r.cfg.kycConfigJSON.Load().Social1KYC.xPostPatternTemplate
case users.Social2KYCStep:
tmpl = r.cfg.kycConfigJSON.Load().Social2KYC.xPostPatternTemplate
default:
for _, val := range r.cfg.kycConfigJSON.Load().DynamicDistributionSocialKYC {
if val != nil && val.KYCStep == metadata.KYCStep {
tmpl = val.xPostPatternTemplate

break
}
}
}
if tmpl != nil {
bf := new(bytes.Buffer)
log.Panic(errors.Wrapf(tmpl.Execute(bf, user), "failed to execute expectedPostSubtext template for metadata:%+v user:%+v", metadata, user))

return bf.String()
}
}

return ""
}

func (r *repository) expectedPostURL(metadata *VerificationMetadata) (url string) {
if metadata.Social == TwitterType {
switch metadata.KYCStep { //nolint:exhaustive // Not needed. Everything else is validated before this.
Expand Down

0 comments on commit f0d0e4d

Please sign in to comment.