Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev to main #200

Merged
merged 7 commits into from
Mar 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions call-profile/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/aws/aws-lambda-go v1.41.0
github.com/aws/aws-sdk-go v1.44.284
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
github.com/golang-jwt/jwt/v5 v5.2.0
golang.org/x/crypto v0.14.0
google.golang.org/api v0.128.0
)
Expand Down
2 changes: 2 additions & 0 deletions call-profile/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew=
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
Expand Down
90 changes: 72 additions & 18 deletions call-profile/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"os"
"sync"
"time"
"bytes"

"cloud.google.com/go/firestore"
firebase "firebase.google.com/go"
Expand All @@ -25,6 +26,7 @@ import (
// validation packages
"github.com/go-ozzo/ozzo-validation/v4"
"github.com/go-ozzo/ozzo-validation/v4/is"
"github.com/golang-jwt/jwt/v5"
)

var wg sync.WaitGroup
Expand Down Expand Up @@ -87,6 +89,10 @@ type structProfilesSkipped struct {
OtherError []string
}

type Claims struct {
jwt.RegisteredClaims
}

/*
Structures Conversions
*/
Expand Down Expand Up @@ -155,6 +161,8 @@ var Constants map[string]string = map[string]string{
"ENV_PRODUCTION": "PRODUCTION",
"STORED": "stored",
"FIRE_STORE_CRED": "firestoreCred",
"DISCORD_BOT_URL": "discordBotURL",
"IDENTITY_SERVICE_PRIVATE_KEY": "identityServicePrivateKey",
"PROFILE_SERVICE_HEALTH": "PROFILE_SERVICE_HEALTH",
"PROFILE_SKIPPED": "PROFILE_SKIPPED",
"PROFILE_DIFF_STORED": "PROFILE_DIFF_STORED",
Expand All @@ -173,11 +181,11 @@ var Constants map[string]string = map[string]string{
/*
Setting Firestore Key for development/production
*/
func getFirestoreKey() string {
func getParameter(parameter string) string {
if os.Getenv(("environment")) == Constants["ENV_DEVELOPMENT"] {
return os.Getenv(Constants["FIRE_STORE_CRED"])
return os.Getenv(parameter)
} else if os.Getenv(("environment")) == Constants["ENV_PRODUCTION"] {
var parameterName string = Constants["FIRE_STORE_CRED"]
var parameterName string = parameter

sess := session.Must(session.NewSessionWithOptions(session.Options{
SharedConfigState: session.SharedConfigEnable,
Expand Down Expand Up @@ -206,7 +214,7 @@ func getFirestoreKey() string {
Function to initialize the firestore client
*/
func initializeFirestoreClient(ctx context.Context) (*firestore.Client, error) {
sa := option.WithCredentialsJSON([]byte(getFirestoreKey()))
sa := option.WithCredentialsJSON([]byte(getParameter(Constants["FIRE_STORE_CRED"])))
app, err := firebase.NewApp(ctx, nil, sa)
if err != nil {
return nil, err
Expand Down Expand Up @@ -234,6 +242,29 @@ func (res Res) Validate() error {
validation.Field(&res.Website, is.URL))
}

/*
Functions to generate jwt token
*/

func generateJWTToken() string {
signKey, errGeneratingRSAKey := jwt.ParseRSAPrivateKeyFromPEM([]byte(getParameter(Constants["IDENTITY_SERVICE_PRIVATE_KEY"])))
if(errGeneratingRSAKey != nil) {
return "";
}
expirationTime := time.Now().Add(1 * time.Minute)
t := jwt.New(jwt.GetSigningMethod("RS256"))
t.Claims = &Claims{
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expirationTime),
},
}
tokenString, err := t.SignedString(signKey)
if(err != nil) {
return "";
}
return tokenString
}

/*
MODELS
*/
Expand Down Expand Up @@ -294,12 +325,28 @@ func logProfileStored(client *firestore.Client, ctx context.Context, userId stri
/*
Function for setting the profileStatus in user object in firestore
*/
func setProfileStatusBlocked(client *firestore.Client, ctx context.Context, userId string, reason string, sessionId string) {
func setProfileStatusBlocked(client *firestore.Client, ctx context.Context, userId string, reason string, sessionId string, discordId string) {
client.Collection("users").Doc(userId).Set(ctx, map[string]interface{}{
"profileStatus": Constants["STATUS_BLOCKED"],
"chaincode": "",
}, firestore.MergeAll)

if discordId != "" {
tokenString := generateJWTToken();
postBody, _ := json.Marshal(map[string]string{
"userId": discordId,
"reason": reason,
})

responseBody := bytes.NewBuffer(postBody)

httpClient := &http.Client{}
req, _ := http.NewRequest("POST", os.Getenv(Constants["DISCORD_BOT_URL"]) + "/profile/blocked", responseBody)
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tokenString))
httpClient.Do(req)
}

newLog := Log{
Type: Constants["PROFILE_SERVICE_BLOCKED"],
Timestamp: time.Now(),
Expand Down Expand Up @@ -364,13 +411,13 @@ func generateAndStoreDiff(client *firestore.Client, ctx context.Context, res Res
/*
Getting data from the user's service
*/
func getdata(client *firestore.Client, ctx context.Context, userId string, userUrl string, chaincode string, userData Res, sessionId string) string {
func getdata(client *firestore.Client, ctx context.Context, userId string, userUrl string, chaincode string, userData Res, sessionId string, discordId string) string {
var status string = ""
userUrl = userUrl + "profile"
hashedChaincode, err := bcrypt.GenerateFromPassword([]byte(chaincode), bcrypt.DefaultCost)
if err != nil {
logProfileSkipped(client, ctx, userId, fmt.Sprintln(err), sessionId)
setProfileStatusBlocked(client, ctx, userId, fmt.Sprintln(err), sessionId)
setProfileStatusBlocked(client, ctx, userId, fmt.Sprintln(err), sessionId, discordId)
return "chaincode not encrypted"
}

Expand All @@ -380,18 +427,18 @@ func getdata(client *firestore.Client, ctx context.Context, userId string, userU
resp, err := httpClient.Do(req)
if err != nil {
logProfileSkipped(client, ctx, userId, fmt.Sprintln(err), sessionId)
setProfileStatusBlocked(client, ctx, userId, fmt.Sprintln(err), sessionId)
setProfileStatusBlocked(client, ctx, userId, fmt.Sprintln(err), sessionId, discordId)
return "error getting profile data"
}
if resp.StatusCode == 401 {
logProfileSkipped(client, ctx, userId, "Unauthenticated Access to Profile Data", sessionId)
setProfileStatusBlocked(client, ctx, userId, "Unauthenticated Access to Profile Data", sessionId)
setProfileStatusBlocked(client, ctx, userId, "Unauthenticated Access to Profile Data", sessionId, discordId)
resp.Body.Close()
return "unauthenticated access to profile data"
}
if resp.StatusCode != 200 {
logProfileSkipped(client, ctx, userId, "Error in getting Profile Data", sessionId)
setProfileStatusBlocked(client, ctx, userId, "Error in getting Profile Data", sessionId)
setProfileStatusBlocked(client, ctx, userId, "Error in getting Profile Data", sessionId, discordId)
resp.Body.Close()
return "error in getting profile data"
}
Expand All @@ -401,22 +448,22 @@ func getdata(client *firestore.Client, ctx context.Context, userId string, userU
r, err := ioutil.ReadAll(resp.Body)
if err != nil {
logProfileSkipped(client, ctx, userId, fmt.Sprintln(err), sessionId)
setProfileStatusBlocked(client, ctx, userId, fmt.Sprintln(err), sessionId)
setProfileStatusBlocked(client, ctx, userId, fmt.Sprintln(err), sessionId, discordId)
return "error reading profile data"
}
var res Res
err = json.Unmarshal([]byte(r), &res)
if err != nil {
logProfileSkipped(client, ctx, userId, fmt.Sprintln(err), sessionId)
setProfileStatusBlocked(client, ctx, userId, fmt.Sprintln(err), sessionId)
setProfileStatusBlocked(client, ctx, userId, fmt.Sprintln(err), sessionId, discordId)
return "error converting data to json"
}

err = res.Validate()

if err != nil {
logProfileSkipped(client, ctx, userId, fmt.Sprintln(err), sessionId)
setProfileStatusBlocked(client, ctx, userId, fmt.Sprintln(err), sessionId)
setProfileStatusBlocked(client, ctx, userId, fmt.Sprintln(err), sessionId, discordId)
return fmt.Sprintf("error in validation: ", err)
}

Expand Down Expand Up @@ -481,12 +528,19 @@ func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyRespo

var userUrl string
var chaincode string
var discordId string

if str, ok := dsnap.Data()["discordId"].(string); ok {
discordId = str
} else {
discordId = ""
}

if str, ok := dsnap.Data()["profileURL"].(string); ok {
userUrl = str
} else {
logProfileSkipped(client, ctx, userId, "Profile URL not available", sessionId)
setProfileStatusBlocked(client, ctx, userId, "Profile URL not available", sessionId)
setProfileStatusBlocked(client, ctx, userId, "Profile URL not available", sessionId, discordId)
return events.APIGatewayProxyResponse{
Body: "Profile Skipped No Profile URL",
StatusCode: 200,
Expand All @@ -496,7 +550,7 @@ func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyRespo
if str, ok := dsnap.Data()["chaincode"].(string); ok {
if str == "" {
logProfileSkipped(client, ctx, userId, "Profile Service Blocked or Chaincode is empty", sessionId)
setProfileStatusBlocked(client, ctx, userId, "Profile Service Blocked or Chaincode is empty", sessionId)
setProfileStatusBlocked(client, ctx, userId, "Profile Service Blocked or Chaincode is empty", sessionId, discordId)
return events.APIGatewayProxyResponse{
Body: "Profile Skipped Profile Service Blocked",
StatusCode: 200,
Expand All @@ -505,7 +559,7 @@ func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyRespo
chaincode = str
} else {
logProfileSkipped(client, ctx, userId, "Chaincode Not Found", sessionId)
setProfileStatusBlocked(client, ctx, userId, "Chaincode Not Found", sessionId)
setProfileStatusBlocked(client, ctx, userId, "Chaincode Not Found", sessionId, discordId)
return events.APIGatewayProxyResponse{
Body: "Profile Skipped Chaincode Not Found",
StatusCode: 200,
Expand Down Expand Up @@ -539,14 +593,14 @@ func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyRespo
logHealth(client, ctx, userId, isServiceRunning, sessionId)
if !isServiceRunning {
logProfileSkipped(client, ctx, userId, "Profile Service Down", sessionId)
setProfileStatusBlocked(client, ctx, userId, "Profile Service Down", sessionId)
setProfileStatusBlocked(client, ctx, userId, "Profile Service Down", sessionId, discordId)
return events.APIGatewayProxyResponse{
Body: "Profile Skipped Service Down",
StatusCode: 200,
}, nil
}

dataErr := getdata(client, ctx, userId, userUrl, chaincode, diffToRes(userData), sessionId)
dataErr := getdata(client, ctx, userId, userUrl, chaincode, diffToRes(userData), sessionId, discordId)
if dataErr != "" {
return events.APIGatewayProxyResponse{
Body: "Profile Skipped " + dataErr,
Expand Down
2 changes: 2 additions & 0 deletions template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Globals:
firestoreCred: YourFirestoreCredentials
environment: PRODUCTION
baseURL: YourBaseAPIURL
discordBotURL: DiscordBotURL
identityServicePrivateKey: YourIdentityServicePrivateKey

Resources:
HealthFunction:
Expand Down
9 changes: 6 additions & 3 deletions verify/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ func TestGetUserIdFromBody(t *testing.T) {
type testVerifyData struct {
name string
path string
salt string
chaincode string
mockStatusCode int
mockResBody string
Expand All @@ -174,15 +175,17 @@ func TestVerify(t *testing.T) {
{
name: "VERIFIED",
path: "/profile-one",
salt: "testSalt",
chaincode: "testchaincode",
mockStatusCode: http.StatusOK,
mockResBody: `{"hash": "$2a$12$ScGc2Q0t0rqqSJK1E2W/WuaRVAchaVWdUqb1hQi21cFTnOVvlIdry"}`,
mockResBody: `{"hash": "cadf727ffff23ec46c17d808a4884ea7566765182d1a2ffa88e4719bc1f7f9fb328e2abacc13202f2dc55b9d653919b79ecf02dd752de80285bbec57a57713d9"}`,
expectedStatus: "VERIFIED",
expectedErr: nil,
},
{
name: "BLOCKED",
path: "/profile-two",
salt: "testSalt",
chaincode: "invalid",
mockStatusCode: http.StatusForbidden,
mockResBody: `{"hash": "abcdefghijklmnopqrstuvwxyz"}`,
Expand All @@ -194,7 +197,7 @@ func TestVerify(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/profile-one" {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"hash": "$2a$12$ScGc2Q0t0rqqSJK1E2W/WuaRVAchaVWdUqb1hQi21cFTnOVvlIdry"}`))
w.Write([]byte(`{"hash": "cadf727ffff23ec46c17d808a4884ea7566765182d1a2ffa88e4719bc1f7f9fb328e2abacc13202f2dc55b9d653919b79ecf02dd752de80285bbec57a57713d9"}`))
}

if r.URL.Path == "/profile-two" {
Expand All @@ -206,7 +209,7 @@ func TestVerify(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {

status, err := verify(server.URL+testCase.path, testCase.chaincode)
status, err := verify(server.URL+testCase.path, testCase.chaincode, testCase.salt)

assert.Equal(t, testCase.expectedStatus, status)
assert.Equal(t, testCase.expectedErr, err)
Expand Down
16 changes: 9 additions & 7 deletions verify/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,27 +225,24 @@ func randSalt(n int) string {
/*
Function to verify the user
*/
func verify(profileURL string, chaincode string) (string, error) {
func verify(profileURL string, chaincode string, salt string) (string, error) {
type res struct {
Hash string `json:"hash"`
}

rand.Seed(time.Now().UnixNano())
var salt string = randSalt(21)

postBody, _ := json.Marshal(map[string]string{
"salt": salt,
})

responseBody := bytes.NewBuffer(postBody)
resp, err := http.Post(profileURL, "application/json", responseBody)
if err != nil {
return "", err
return "BLOCKED", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
return "BLOCKED", err
}
var re res
json.Unmarshal([]byte(body), &re)
Expand Down Expand Up @@ -284,8 +281,13 @@ func (d *deps) handler(request events.APIGatewayProxyRequest) (events.APIGateway
}, nil
}

status, err := verify(profileURL, chaincode)
rand.Seed(time.Now().UnixNano())
var salt string = randSalt(21)

status, err := verify(profileURL, chaincode, salt)
if err != nil {
logVerification(d.client, d.ctx, status, profileURL, userId)
setProfileStatus(d.client, d.ctx, userId, status)
return events.APIGatewayProxyResponse{}, err
}
logVerification(d.client, d.ctx, status, profileURL, userId)
Expand Down
Loading