diff --git a/call-profile/go.mod b/call-profile/go.mod index 1206540..19b1ca8 100644 --- a/call-profile/go.mod +++ b/call-profile/go.mod @@ -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 ) diff --git a/call-profile/go.sum b/call-profile/go.sum index ad3795f..ae7c36a 100644 --- a/call-profile/go.sum +++ b/call-profile/go.sum @@ -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= diff --git a/call-profile/main.go b/call-profile/main.go index 5725d49..80389ef 100644 --- a/call-profile/main.go +++ b/call-profile/main.go @@ -10,6 +10,7 @@ import ( "os" "sync" "time" + "bytes" "cloud.google.com/go/firestore" firebase "firebase.google.com/go" @@ -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 @@ -87,6 +89,10 @@ type structProfilesSkipped struct { OtherError []string } +type Claims struct { + jwt.RegisteredClaims +} + /* Structures Conversions */ @@ -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", @@ -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, @@ -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 @@ -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 */ @@ -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(), @@ -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" } @@ -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" } @@ -401,14 +448,14 @@ 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" } @@ -416,7 +463,7 @@ func getdata(client *firestore.Client, ctx context.Context, userId string, userU 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) } @@ -481,22 +528,29 @@ func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyRespo var userUrl string var chaincode string + var discordId string 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, }, nil } + if str, ok := dsnap.Data()["discordId"].(string); ok { + discordId = str + } else { + discordId = "" + } + 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, @@ -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, @@ -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, diff --git a/template.yaml b/template.yaml index 9647273..a373f02 100644 --- a/template.yaml +++ b/template.yaml @@ -15,6 +15,8 @@ Globals: firestoreCred: YourFirestoreCredentials environment: PRODUCTION baseURL: YourBaseAPIURL + discordBotURL: DiscordBotURL + identityServicePrivateKey: YourIdentityServicePrivateKey Resources: HealthFunction: