From 5872be53c61e773a1c3c0175744547930bef099f Mon Sep 17 00:00:00 2001 From: ice-myles <96409608+ice-myles@users.noreply.github.com> Date: Fri, 22 Nov 2024 12:44:41 +0300 Subject: [PATCH] CMC post link duplicate check. --- go.mod | 6 +++--- go.sum | 12 +++++------ kyc/social/DDL.sql | 5 +++++ kyc/social/contract.go | 1 + kyc/social/social.go | 20 ++++++++++--------- kyc/social/social_test.go | 6 +++--- .../verification_scenarios.go | 4 ++++ 7 files changed, 33 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 3e0ca39c..3581c321 100644 --- a/go.mod +++ b/go.mod @@ -31,8 +31,8 @@ require ( require ( cel.dev/expr v0.18.0 // indirect cloud.google.com/go v0.116.0 // indirect - cloud.google.com/go/auth v0.10.2 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.5 // indirect + cloud.google.com/go/auth v0.11.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect cloud.google.com/go/compute/metadata v0.5.2 // indirect cloud.google.com/go/firestore v1.17.0 // indirect cloud.google.com/go/iam v1.2.2 // indirect @@ -184,7 +184,7 @@ require ( golang.org/x/text v0.20.0 // indirect golang.org/x/time v0.8.0 // indirect golang.org/x/tools v0.27.0 // indirect - google.golang.org/api v0.208.0 // indirect + google.golang.org/api v0.209.0 // indirect google.golang.org/appengine/v2 v2.0.6 // indirect google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 // indirect diff --git a/go.sum b/go.sum index 6c6ac0d6..02ecdf12 100644 --- a/go.sum +++ b/go.sum @@ -3,10 +3,10 @@ cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE= cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U= -cloud.google.com/go/auth v0.10.2 h1:oKF7rgBfSHdp/kuhXtqU/tNDr0mZqhYbEh+6SiqzkKo= -cloud.google.com/go/auth v0.10.2/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI= -cloud.google.com/go/auth/oauth2adapt v0.2.5 h1:2p29+dePqsCHPP1bqDJcKj4qxRyYCcbzKpFyKGt3MTk= -cloud.google.com/go/auth/oauth2adapt v0.2.5/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= +cloud.google.com/go/auth v0.11.0 h1:Ic5SZz2lsvbYcWT5dfjNWgw6tTlGi2Wc8hyQSC9BstA= +cloud.google.com/go/auth v0.11.0/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI= +cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU= +cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo= cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= cloud.google.com/go/firestore v1.17.0 h1:iEd1LBbkDZTFsLw3sTH50eyg4qe8eoG6CjocmEXO9aQ= @@ -563,8 +563,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.208.0 h1:8Y62MUGRviQnnP9/41/bYAGySPKAN9iwzV96ZvhwyVE= -google.golang.org/api v0.208.0/go.mod h1:I53S168Yr/PNDNMi5yPnDc0/LGRZO6o7PoEbl/HY3CM= +google.golang.org/api v0.209.0 h1:Ja2OXNlyRlWCWu8o+GgI4yUn/wz9h/5ZfFbKz+dQX+w= +google.golang.org/api v0.209.0/go.mod h1:I53S168Yr/PNDNMi5yPnDc0/LGRZO6o7PoEbl/HY3CM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine/v2 v2.0.6 h1:LvPZLGuchSBslPBp+LAhihBeGSiRh1myRoYK4NtuBIw= diff --git a/kyc/social/DDL.sql b/kyc/social/DDL.sql index 04663cab..dd32029a 100644 --- a/kyc/social/DDL.sql +++ b/kyc/social/DDL.sql @@ -42,6 +42,11 @@ CREATE TABLE IF NOT EXISTS socials ( social text NOT NULL CHECK (social = 'twitter' OR social = 'facebook'), user_handle text NOT NULL, PRIMARY KEY (social, user_handle)); +ALTER TABLE socials +DROP CONSTRAINT socials_social_check; +ALTER TABLE socials +ADD CONSTRAINT socials_social_check +CHECK (social = 'twitter' OR social = 'facebook' OR social = 'cmc'); ALTER TABLE socials DROP CONSTRAINT socials_pkey, ADD PRIMARY KEY (social, user_handle); ALTER TABLE socials DROP CONSTRAINT IF EXISTS socials_social_user_handle_key; diff --git a/kyc/social/contract.go b/kyc/social/contract.go index 0eb8f652..7f466a6e 100644 --- a/kyc/social/contract.go +++ b/kyc/social/contract.go @@ -78,6 +78,7 @@ type ( VerifyPost(ctx context.Context, metadata *VerificationMetadata) (*Verification, error) SkipVerification(ctx context.Context, kycStep users.KYCStep, userID string) error ExpectedPostTemplateText(user *users.User, vm *VerificationMetadata) string + SaveSocial(ctx context.Context, socialType Type, userID, userHandle string) error } UserRepository interface { GetUserByID(ctx context.Context, userID string) (*users.UserProfile, error) diff --git a/kyc/social/social.go b/kyc/social/social.go index 770875f6..dbed22ac 100644 --- a/kyc/social/social.go +++ b/kyc/social/social.go @@ -220,7 +220,7 @@ func (r *repository) VerifyPost(ctx context.Context, metadata *VerificationMetad } if userHandle != "" { //nolint:nestif // . userHandle = strings.ToLower(userHandle) - if err = r.saveSocial(ctx, metadata.Social, metadata.UserID, userHandle); err != nil { + if err = r.SaveSocial(ctx, metadata.Social, metadata.UserID, userHandle); err != nil { if storage.IsErr(err, storage.ErrDuplicate) { log.Error(errors.Wrapf(err, "[duplicate]social verification failed for KYCStep:%v,Social:%v,Language:%v,userID:%v,userHandle:%v", metadata.KYCStep, metadata.Social, metadata.Language, metadata.UserID, userHandle)) @@ -335,16 +335,18 @@ func (r *repository) saveUnsuccessfulAttempt(ctx context.Context, now *time.Time return errors.Wrapf(err, "failed to `%v`; kycStep:%v,userId:%v,social:%v,reason:%v", sql, metadata.KYCStep, metadata.UserID, metadata.Social, reason) } -func (r *repository) saveSocial(ctx context.Context, socialType Type, userID, userHandle string) error { +func (r *repository) SaveSocial(ctx context.Context, socialType Type, userID, userHandle string) error { sql := `INSERT INTO socials(user_id,social,user_handle) VALUES ($1,$2,$3)` _, err := storage.Exec(ctx, r.db, sql, userID, socialType, userHandle) - if err != nil && storage.IsErr(err, storage.ErrDuplicate, "pk") { - sql = `SELECT true AS bogus WHERE EXISTS (SELECT 1 FROM socials WHERE user_id = $1 AND social = $2 AND lower(user_handle) = $3)` - if _, err2 := storage.ExecOne[struct{ Bogus bool }](ctx, r.db, sql, userID, socialType, userHandle); err2 == nil { - return nil - } else if !storage.IsErr(err2, storage.ErrNotFound) { - err = errors.Wrapf(err2, "failed to check if user used the same userhandle previously; userID:%v, social:%v, userHandle:%v", - userID, socialType, userHandle) + if socialType != CMCType { + if err != nil && storage.IsErr(err, storage.ErrDuplicate, "pk") { + sql = `SELECT true AS bogus WHERE EXISTS (SELECT 1 FROM socials WHERE user_id = $1 AND social = $2 AND lower(user_handle) = $3)` + if _, err2 := storage.ExecOne[struct{ Bogus bool }](ctx, r.db, sql, userID, socialType, userHandle); err2 == nil { + return nil + } else if !storage.IsErr(err2, storage.ErrNotFound) { + err = errors.Wrapf(err2, "failed to check if user used the same userhandle previously; userID:%v, social:%v, userHandle:%v", + userID, socialType, userHandle) + } } } diff --git a/kyc/social/social_test.go b/kyc/social/social_test.go index 7e193f7e..93b5faa6 100644 --- a/kyc/social/social_test.go +++ b/kyc/social/social_test.go @@ -34,15 +34,15 @@ func TestSocialSave(t *testing.T) { helperRemoveSocials(t, db, userName) t.Run("OK", func(t *testing.T) { - err := repo.saveSocial(ctx, TwitterType, userName, "foo") + err := repo.SaveSocial(ctx, TwitterType, userName, "foo") require.NoError(t, err) - err = repo.saveSocial(ctx, TwitterType, userName, "bar") + err = repo.SaveSocial(ctx, TwitterType, userName, "bar") require.NoError(t, err) }) t.Run("Duplicate", func(t *testing.T) { - err := repo.saveSocial(ctx, TwitterType, userName, "foo") + err := repo.SaveSocial(ctx, TwitterType, userName, "foo") require.ErrorIs(t, err, storage.ErrDuplicate) reason := DetectReason(terror.New(err, map[string]any{"user_handle": "foo"})) diff --git a/kyc/verification_scenarios/verification_scenarios.go b/kyc/verification_scenarios/verification_scenarios.go index 9cdc6c3d..5f0ec9e5 100644 --- a/kyc/verification_scenarios/verification_scenarios.go +++ b/kyc/verification_scenarios/verification_scenarios.go @@ -402,6 +402,10 @@ func (r *repository) VerifyCMC(ctx context.Context, metadata *VerificationMetada return errors.Wrapf(ErrVerificationNotPassed, "can't verify post for cmc verifier userID:%v,reason:%v", metadata.UserID, err.Error()) } + if sErr := r.socialRepo.SaveSocial(ctx, social.CMCType, metadata.UserID, metadata.CMCProfileLink); sErr != nil { + return errors.Wrapf(ErrVerificationNotPassed, + "can't save social for userID:%v for post link: %v, reason: %v", metadata.UserID, metadata.CMCProfileLink, social.DetectReason(sErr)) + } return nil }