Ver código fonte

Merge pull request #3120 from gravitl/ACC-638

ACC-638: SaaS user mgmt compatibility changes
Abhishek K 1 ano atrás
pai
commit
ddfd1e8c5b

+ 7 - 2
.github/workflows/branchtest.yml

@@ -2,6 +2,11 @@ name: Deploy and Test Branch
 
 on:
   workflow_dispatch:
+    inputs:
+      branches:
+        description: 'Branch to deploy and test'
+        required: true
+        default: 'develop'
   pull_request:
       types: [opened, synchronize, reopened]
       branches: [develop]
@@ -28,7 +33,7 @@ jobs:
         uses: actions/checkout@v4
         with:
           repository: gravitl/netclient
-          ref: develop
+          ref: ${{ github.event_name == 'workflow_dispatch' && inputs.branch || 'develop' }}
       - name: check if branch exists
         id: getbranch 
         run: |
@@ -45,6 +50,6 @@ jobs:
     needs: [getbranch, skip-check]
     with:
       netclientbranch: ${{ needs.getbranch.outputs.netclientbranch }}
-      netmakerbranch: ${{ github.head_ref }}
+      netmakerbranch: ${{ github.event_name == 'workflow_dispatch' && inputs.branch || github.head_ref }}
       tag: ${{ github.run_id }}-${{ github.run_attempt }}
     secrets: inherit          

+ 22 - 6
.github/workflows/deletedroplets.yml

@@ -37,7 +37,7 @@ jobs:
       - name: delete droplets
         if: success() || failure()
         run: |
-          sleep 5m
+          sleep 1m
           response=$(curl -X DELETE \
             -H "Content-Type: application/json" \
             -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
@@ -56,8 +56,9 @@ jobs:
             echo "Failed to delete droplets. Status code: $status_code"
             exit 1
           fi
+          sleep 1m
         env:
-          DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }}
+          DIGITALOCEAN_TOKEN: ${{ secrets.DO_TEST_TOKEN }}
           TAG: ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}
       - name: mark server as available
         if: success() || failure()
@@ -108,13 +109,28 @@ jobs:
       - name: delete droplets
         if: success() || failure()
         run: |
-          sleep 3h
-          curl -X DELETE \
+          sleep 1m
+          response=$(curl -X DELETE \
             -H "Content-Type: application/json" \
             -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
-            "https://api.digitalocean.com/v2/droplets?tag_name=$TAG"
+            -w "\n%{http_code}" \
+            "https://api.digitalocean.com/v2/droplets?tag_name=$TAG")
+          
+          status_code=$(echo "$response" | tail -n1)
+          body=$(echo "$response" | sed '$d')
+          
+          echo "Response body: $body"
+          echo "Status code: $status_code"
+          
+          if [ "$status_code" -eq 204 ]; then
+            echo "Droplets deleted successfully"
+          else
+            echo "Failed to delete droplets. Status code: $status_code"
+            exit 1
+          fi
+          sleep 1m
         env:
-          DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }}
+          DIGITALOCEAN_TOKEN: ${{ secrets.DO_TEST_TOKEN }}
           TAG: ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}
       - name: mark server as available
         if: success() || failure()

+ 12 - 0
pro/controllers/users.go

@@ -206,6 +206,10 @@ func inviteUsers(w http.ResponseWriter, r *http.Request) {
 	}
 	for _, inviteeEmail := range inviteReq.UserEmails {
 		// check if user with email exists, then ignore
+		if !email.IsValid(inviteeEmail) {
+			logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid email "+inviteeEmail), "badrequest"))
+			return
+		}
 		_, err := logic.GetUser(inviteeEmail)
 		if err == nil {
 			// user exists already, so ignore
@@ -228,6 +232,14 @@ func inviteUsers(w http.ResponseWriter, r *http.Request) {
 			slog.Error("failed to parse to invite url", "error", err)
 			return
 		}
+		if servercfg.DeployedByOperator() {
+			u, err = url.Parse(fmt.Sprintf("%s/invite?tenant_id=%s&email=%s&invite_code=%s",
+				proLogic.GetAccountsUIHost(), url.QueryEscape(servercfg.GetNetmakerTenantID()), url.QueryEscape(invite.Email), url.QueryEscape(invite.InviteCode)))
+			if err != nil {
+				slog.Error("failed to parse to invite url", "error", err)
+				return
+			}
+		}
 		invite.InviteURL = u.String()
 		err = logic.InsertUserInvite(invite)
 		if err != nil {

+ 6 - 0
pro/email/email.go

@@ -2,6 +2,7 @@ package email
 
 import (
 	"context"
+	"regexp"
 
 	"github.com/gravitl/netmaker/servercfg"
 )
@@ -52,3 +53,8 @@ type Notification struct {
 func GetClient() (e EmailSender) {
 	return client
 }
+
+func IsValid(email string) bool {
+	emailRegex := regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`)
+	return emailRegex.MatchString(email)
+}

+ 2 - 12
pro/license.go

@@ -20,6 +20,7 @@ import (
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/ncutils"
+	proLogic "github.com/gravitl/netmaker/pro/logic"
 	"github.com/gravitl/netmaker/servercfg"
 )
 
@@ -206,7 +207,7 @@ func validateLicenseKey(encryptedData []byte, publicKey *[32]byte) ([]byte, bool
 
 	req, err := http.NewRequest(
 		http.MethodPost,
-		getAccountsHost()+"/api/v1/license/validate",
+		proLogic.GetAccountsHost()+"/api/v1/license/validate",
 		bytes.NewReader(requestBody),
 	)
 	if err != nil {
@@ -255,17 +256,6 @@ func validateLicenseKey(encryptedData []byte, publicKey *[32]byte) ([]byte, bool
 	return nil, false, err
 }
 
-func getAccountsHost() string {
-	switch servercfg.GetEnvironment() {
-	case "dev":
-		return accountsHostDevelopment
-	case "staging":
-		return accountsHostStaging
-	default:
-		return accountsHostProduction
-	}
-}
-
 func cacheResponse(response []byte) error {
 	lrc := licenseResponseCache{
 		Body: response,

+ 6 - 4
pro/license_test.go

@@ -4,11 +4,13 @@
 package pro
 
 import (
-	"github.com/gravitl/netmaker/config"
 	"testing"
+
+	"github.com/gravitl/netmaker/config"
+	proLogic "github.com/gravitl/netmaker/pro/logic"
 )
 
-func Test_getAccountsHost(t *testing.T) {
+func Test_GetAccountsHost(t *testing.T) {
 	tests := []struct {
 		name string
 		envK string
@@ -69,8 +71,8 @@ func Test_getAccountsHost(t *testing.T) {
 			if tt.envK != "" {
 				t.Setenv(tt.envK, tt.envV)
 			}
-			if got := getAccountsHost(); got != tt.want {
-				t.Errorf("getAccountsHost() = %v, want %v", got, tt.want)
+			if got := proLogic.GetAccountsHost(); got != tt.want {
+				t.Errorf("GetAccountsHost() = %v, want %v", got, tt.want)
 			}
 		})
 	}

+ 43 - 0
pro/logic/security.go

@@ -7,6 +7,27 @@ import (
 
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/servercfg"
+)
+
+// constants for accounts api hosts
+const (
+	// accountsHostDevelopment is the accounts api host for development environment
+	accountsHostDevelopment = "https://api.dev.accounts.netmaker.io"
+	// accountsHostStaging is the accounts api host for staging environment
+	accountsHostStaging = "https://api.staging.accounts.netmaker.io"
+	// accountsHostProduction is the accounts api host for production environment
+	accountsHostProduction = "https://api.accounts.netmaker.io"
+)
+
+// constants for accounts UI hosts
+const (
+	// accountsUIHostDevelopment is the accounts UI host for development environment
+	accountsUIHostDevelopment = "https://account.dev.netmaker.io"
+	// accountsUIHostStaging is the accounts UI host for staging environment
+	accountsUIHostStaging = "https://account.staging.netmaker.io"
+	// accountsUIHostProduction is the accounts UI host for production environment
+	accountsUIHostProduction = "https://account.netmaker.io"
 )
 
 func NetworkPermissionsCheck(username string, r *http.Request) error {
@@ -186,3 +207,25 @@ func checkPermissionScopeWithReqMethod(scope models.RsrcPermissionScope, reqmeth
 	}
 	return errors.New("operation not permitted")
 }
+
+func GetAccountsHost() string {
+	switch servercfg.GetEnvironment() {
+	case "dev":
+		return accountsHostDevelopment
+	case "staging":
+		return accountsHostStaging
+	default:
+		return accountsHostProduction
+	}
+}
+
+func GetAccountsUIHost() string {
+	switch servercfg.GetEnvironment() {
+	case "dev":
+		return accountsUIHostDevelopment
+	case "staging":
+		return accountsUIHostStaging
+	default:
+		return accountsUIHostProduction
+	}
+}

+ 0 - 1
pro/logic/user_mgmt.go

@@ -380,7 +380,6 @@ func DeleteRole(rid models.UserRoleID, force bool) error {
 			}
 		}
 	}
-
 	return database.DeleteRecord(database.USER_PERMISSIONS_TABLE_NAME, rid.String())
 }
 

+ 0 - 10
pro/types.go

@@ -7,16 +7,6 @@ import (
 	"fmt"
 )
 
-// constants for accounts api hosts
-const (
-	// accountsHostDevelopment is the accounts api host for development environment
-	accountsHostDevelopment = "https://api.dev.accounts.netmaker.io"
-	// accountsHostStaging is the accounts api host for staging environment
-	accountsHostStaging = "https://api.staging.accounts.netmaker.io"
-	// accountsHostProduction is the accounts api host for production environment
-	accountsHostProduction = "https://api.accounts.netmaker.io"
-)
-
 const (
 	license_cache_key          = "license_response_cache"
 	license_validation_err_msg = "invalid license"