...

Source file src/edge-infra.dev/pkg/sds/emergencyaccess/rules/server/integration/rules_test.go

Documentation: edge-infra.dev/pkg/sds/emergencyaccess/rules/server/integration

     1  package rulestest
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"database/sql"
     7  	"fmt"
     8  	"io"
     9  	"net/http"
    10  	"net/http/httptest"
    11  	"os"
    12  	"strings"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/gin-gonic/gin"
    17  	_ "github.com/jackc/pgx/v4/stdlib"
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/require"
    20  
    21  	"edge-infra.dev/pkg/lib/fog"
    22  	rulesengine "edge-infra.dev/pkg/sds/emergencyaccess/rules"
    23  	"edge-infra.dev/pkg/sds/emergencyaccess/rules/server"
    24  	"edge-infra.dev/pkg/sds/emergencyaccess/rules/storage/database"
    25  	"edge-infra.dev/test/f2"
    26  	"edge-infra.dev/test/f2/x/postgres"
    27  )
    28  
    29  var f f2.Framework
    30  
    31  func TestMain(m *testing.M) {
    32  	// Set up test framework in TestMain
    33  	f = f2.New(
    34  		context.Background(),
    35  		f2.WithExtensions(
    36  			postgres.New(),
    37  		),
    38  	).
    39  		BeforeEachTest(func(ctx f2.Context, t *testing.T) (f2.Context, error) {
    40  			// Create rulesengine tables
    41  			db := postgres.FromContextT(ctx, t).DB()
    42  			_, err := db.ExecContext(ctx, schema)
    43  			if err != nil {
    44  				return ctx, fmt.Errorf("error creating schema: %w", err)
    45  			}
    46  			return ctx, nil
    47  		})
    48  
    49  	// Run the tests
    50  	os.Exit(f.Run(m))
    51  }
    52  
    53  func TestAdminPrivilegeEndpoints(t *testing.T) {
    54  	var (
    55  		//buf         *bytes.Buffer
    56  		rulesEngine *gin.Engine
    57  	)
    58  
    59  	feat := f2.NewFeature("Admin Privilege Rules").
    60  		Setup("Create Rules Engine server", func(ctx f2.Context, t *testing.T) f2.Context {
    61  			var db = postgres.FromContextT(ctx, t).DB()
    62  			rulesEngine, _ = setupRulesEngine(t, db)
    63  			_ = rulesEngine
    64  			return ctx
    65  		}).
    66  		Setup("Add some data", func(ctx f2.Context, t *testing.T) f2.Context {
    67  			var (
    68  				db = postgres.FromContextT(ctx, t).DB()
    69  			)
    70  
    71  			_, err := db.ExecContext(ctx, priviledgesData)
    72  			require.NoError(t, err)
    73  
    74  			return ctx
    75  		}).
    76  		Test("Get All Privileges", func(ctx f2.Context, t *testing.T) f2.Context {
    77  			test := testCase{
    78  				url:    "/admin/privileges",
    79  				method: http.MethodGet,
    80  
    81  				expectedStatus: http.StatusOK,
    82  				expectedOut: `[
    83  					{
    84  						"name": "read",
    85  						"id": "78587bb1-6ca2-4d2d-a223-1ee642514b97"
    86  					}
    87  				]`,
    88  			}
    89  
    90  			return testEndpoint(ctx, t, rulesEngine, test)
    91  		}).
    92  		Feature()
    93  
    94  	// Run the tests
    95  	f.Test(t, feat)
    96  }
    97  
    98  func setupRulesEngine(t *testing.T, db *sql.DB) (*gin.Engine, *bytes.Buffer) {
    99  	gin.SetMode(gin.TestMode)
   100  	router := gin.New()
   101  
   102  	buf := new(bytes.Buffer)
   103  	log := fog.New(fog.To(buf))
   104  
   105  	ds := database.New(log, db)
   106  	re := rulesengine.New(ds)
   107  	res, err := server.New(router, re, log)
   108  	require.NoError(t, err)
   109  
   110  	return res.GinEngine, buf
   111  }
   112  func TestPostDefaultRuleEndpoints(t *testing.T) {
   113  	var (
   114  		rulesEngine *gin.Engine
   115  		buf         *bytes.Buffer
   116  	)
   117  
   118  	feat := f2.NewFeature("Admin Privilege Rules").
   119  		Setup("Create Rules Engine server", func(ctx f2.Context, t *testing.T) f2.Context {
   120  			var db = postgres.FromContextT(ctx, t).DB()
   121  			rulesEngine, buf = setupRulesEngine(t, db)
   122  			_ = buf
   123  			return ctx
   124  		}).
   125  		Setup("Add some data", func(ctx f2.Context, t *testing.T) f2.Context {
   126  			var (
   127  				db = postgres.FromContextT(ctx, t).DB()
   128  			)
   129  
   130  			for _, q := range rulesData.privExs {
   131  				_, err := db.ExecContext(ctx, q)
   132  				require.NoError(t, err)
   133  			}
   134  
   135  			for _, q := range rulesData.commExs {
   136  				_, err := db.ExecContext(ctx, q)
   137  				require.NoError(t, err)
   138  			}
   139  
   140  			return ctx
   141  		}).
   142  		Test("Post Rule", func(ctx f2.Context, t *testing.T) f2.Context {
   143  			// Post ls [basic]
   144  			test := testCase{
   145  				url:            "/admin/rules/default/commands",
   146  				method:         http.MethodPost,
   147  				body:           strings.NewReader(`[{"command":"ls","privileges":["basic"]}]`),
   148  				expectedStatus: http.StatusOK,
   149  			}
   150  
   151  			ctx = testEndpoint(ctx, t, rulesEngine, test)
   152  			return ctx
   153  		}).
   154  		Test("Read Database State 1", func(ctx f2.Context, t *testing.T) f2.Context {
   155  			// Expect ls [basic]
   156  			expectedOutput := fmt.Sprintf(`[
   157  				{"command":{"name":"ls","id":"%s"},
   158  				"privileges":[{"name":"basic","id":"%s"}]}
   159  				]`, rulesData.comms["ls"], rulesData.privs["basic"])
   160  			return testDefaultRules(ctx, expectedOutput, rulesEngine, t)
   161  		}).
   162  		Test("Post Rule Existing Privilege", func(ctx f2.Context, t *testing.T) f2.Context {
   163  			// Post ls [read]
   164  			test := testCase{
   165  				url:            "/admin/rules/default/commands",
   166  				method:         http.MethodPost,
   167  				body:           strings.NewReader(`[{"command":"ls","privileges":["read"]}]`),
   168  				expectedStatus: http.StatusOK,
   169  			}
   170  
   171  			return testEndpoint(ctx, t, rulesEngine, test)
   172  		}).
   173  		Test("Read Database State 2", func(ctx f2.Context, t *testing.T) f2.Context {
   174  			// Expect ls [basic,read]
   175  			expectedOutput := fmt.Sprintf(`[
   176  				{
   177  					"command":{"name":"ls","id":"%s"},
   178  					"privileges":[
   179  						{"name":"basic","id":"%s"},
   180  						{"name":"read","id":"%s"}
   181  						
   182  						]
   183  				}
   184  				]`,
   185  				rulesData.comms["ls"], rulesData.privs["basic"], rulesData.privs["read"])
   186  			return testDefaultRules(ctx, expectedOutput, rulesEngine, t)
   187  		}).
   188  		Test("Post Rule New and Existing Privilege", func(ctx f2.Context, t *testing.T) f2.Context {
   189  			// Post ls [read,write]
   190  			test := testCase{
   191  				url:            "/admin/rules/default/commands",
   192  				method:         http.MethodPost,
   193  				body:           strings.NewReader(`[{"command":"ls","privileges":["read","write"]}]`),
   194  				expectedStatus: http.StatusOK,
   195  			}
   196  
   197  			return testEndpoint(ctx, t, rulesEngine, test)
   198  		}).
   199  		Test("Read Database State 3", func(ctx f2.Context, t *testing.T) f2.Context {
   200  			// Expect ls [basic,read,write]
   201  			expectedOutput := fmt.Sprintf(`[
   202  				{
   203  					"command":{"name":"ls","id":"%s"},
   204  					"privileges":[
   205  						{"name":"basic","id":"%s"},
   206  						{"name":"read","id":"%s"},
   207  						{"name":"write","id":"%s"}
   208  						
   209  						]
   210  				}
   211  				]`,
   212  				rulesData.comms["ls"],
   213  				rulesData.privs["basic"], rulesData.privs["read"], rulesData.privs["write"])
   214  			return testDefaultRules(ctx, expectedOutput, rulesEngine, t)
   215  		}).
   216  		Test("Post Rule Existing,Invalid,New Privilege", func(ctx f2.Context, t *testing.T) f2.Context {
   217  			// Post ls [read,invalid,admin]
   218  			test := testCase{
   219  				url:            "/admin/rules/default/commands",
   220  				method:         http.MethodPost,
   221  				body:           strings.NewReader(`[{"command":"ls","privileges":["read","invalid","admin"]}]`),
   222  				expectedStatus: http.StatusNotFound,
   223  				expectedOut:    `{"errors":[{"privilege":"invalid", "type":"Unknown Privilege"}]}`,
   224  			}
   225  
   226  			return testEndpoint(ctx, t, rulesEngine, test)
   227  		}).
   228  		Test("Read Database State 4", func(ctx f2.Context, t *testing.T) f2.Context {
   229  			// Expect ls [basic,read,write] -> db state unchanged from read state 3
   230  			expectedOutput := fmt.Sprintf(`[
   231  				{
   232  					"command":{"name":"ls","id":"%s"},
   233  					"privileges":[
   234  						{"name":"basic","id":"%s"},
   235  						{"name":"read","id":"%s"},
   236  						{"name":"write","id":"%s"}
   237  					]
   238  				}
   239  				]`,
   240  				rulesData.comms["ls"],
   241  				rulesData.privs["basic"], rulesData.privs["read"], rulesData.privs["write"])
   242  			return testDefaultRules(ctx, expectedOutput, rulesEngine, t)
   243  		}).
   244  		Test("Post Multiple Rules", func(ctx f2.Context, t *testing.T) f2.Context {
   245  			// Post ls [admin], echo[basic]
   246  			test := testCase{
   247  				url:    "/admin/rules/default/commands",
   248  				method: http.MethodPost,
   249  				body: strings.NewReader(`[
   250  					{"command":"ls","privileges":["admin"]},
   251  					{"command":"echo","privileges":["basic"]}
   252  				]`),
   253  				expectedStatus: http.StatusOK,
   254  			}
   255  
   256  			return testEndpoint(ctx, t, rulesEngine, test)
   257  		}).
   258  		Test("Read Database State 5", func(ctx f2.Context, t *testing.T) f2.Context {
   259  			// Expect ls [basic,read,write,admin] echo [basic]
   260  			expectedOutput := fmt.Sprintf(`[
   261  				{
   262  					"command":{"name":"ls","id":"%s"},
   263  					"privileges":[
   264  						{"name":"basic","id":"%s"},
   265  						{"name":"read","id":"%s"},
   266  						{"name":"write","id":"%s"},
   267  						{"name":"admin","id":"%s"}
   268  
   269  						]
   270  				},
   271  				{
   272  					"command":{"name":"echo","id":"%s"},
   273  					"privileges":[
   274  						{"name":"basic","id":"%s"}
   275  
   276  						]
   277  				}
   278  				]`,
   279  				rulesData.comms["ls"],
   280  				rulesData.privs["basic"], rulesData.privs["read"], rulesData.privs["write"], rulesData.privs["admin"],
   281  				rulesData.comms["echo"],
   282  				rulesData.privs["basic"],
   283  			)
   284  			return testDefaultRules(ctx, expectedOutput, rulesEngine, t)
   285  		}).
   286  		Test("Post Multiple Rules New and Existing Privilege", func(ctx f2.Context, t *testing.T) f2.Context {
   287  			// Post ls [admin], echo[admin]
   288  			test := testCase{
   289  				url:    "/admin/rules/default/commands",
   290  				method: http.MethodPost,
   291  				body: strings.NewReader(`[
   292  					{"command":"ls","privileges":["admin"]},
   293  					{"command":"echo","privileges":["admin"]}
   294  				]`),
   295  				expectedStatus: http.StatusOK,
   296  			}
   297  
   298  			ctx = testEndpoint(ctx, t, rulesEngine, test)
   299  			return ctx
   300  		}).
   301  		Test("Read Database State 6", func(ctx f2.Context, t *testing.T) f2.Context {
   302  			// Expect ls [basic,read,write,admin] echo [basic,admin]
   303  			expectedOutput := fmt.Sprintf(`[
   304  				{
   305  					"command":{"name":"ls","id":"%s"},
   306  					"privileges":[
   307  						{"name":"basic","id":"%s"},
   308  						{"name":"read","id":"%s"},
   309  						{"name":"write","id":"%s"},
   310  						{"name":"admin","id":"%s"}
   311  
   312  						]
   313  				},
   314  				{
   315  					"command":{"name":"echo","id":"%s"},
   316  					"privileges":[
   317  						{"name":"basic","id":"%s"},
   318  						{"name":"admin","id":"%s"}
   319  
   320  						]
   321  				}
   322  				]`,
   323  				rulesData.comms["ls"],
   324  				rulesData.privs["basic"], rulesData.privs["read"], rulesData.privs["write"], rulesData.privs["admin"],
   325  				rulesData.comms["echo"],
   326  				rulesData.privs["basic"], rulesData.privs["admin"],
   327  			)
   328  			return testDefaultRules(ctx, expectedOutput, rulesEngine, t)
   329  		}).
   330  		Test("Post Multiple Rules New, Existing and Invalid Privilege", func(ctx f2.Context, t *testing.T) f2.Context {
   331  			// Post ls [admin, invalid], echo [read]
   332  			test := testCase{
   333  				url:    "/admin/rules/default/commands",
   334  				method: http.MethodPost,
   335  				body: strings.NewReader(`[
   336  					{"command":"ls","privileges":["admin","invalid"]},
   337  					{"command":"echo","privileges":["read"]}
   338  				]`),
   339  				expectedStatus: http.StatusNotFound,
   340  				expectedOut:    ` {"errors":[{"privilege": "invalid", "type":"Unknown Privilege"}]}`,
   341  			}
   342  
   343  			ctx = testEndpoint(ctx, t, rulesEngine, test)
   344  			return ctx
   345  		}).
   346  		Test("Read Database State 7", func(ctx f2.Context, t *testing.T) f2.Context {
   347  			// Expect ls [basic,read,write,admin] echo [basic,admin] -> same as db read state 6
   348  			expectedOutput := fmt.Sprintf(`[
   349  				{
   350  					"command":{"name":"ls","id":"%s"},
   351  					"privileges":[
   352  						{"name":"basic","id":"%s"},
   353  						{"name":"read","id":"%s"},
   354  						{"name":"write","id":"%s"},
   355  						{"name":"admin","id":"%s"}
   356  						]
   357  				},
   358  				{
   359  					"command":{"name":"echo","id":"%s"},
   360  					"privileges":[
   361  						{"name":"basic","id":"%s"},
   362  						{"name":"admin","id":"%s"}
   363  
   364  						]
   365  				}
   366  				]`,
   367  				rulesData.comms["ls"],
   368  				rulesData.privs["basic"], rulesData.privs["read"], rulesData.privs["write"], rulesData.privs["admin"],
   369  				rulesData.comms["echo"],
   370  				rulesData.privs["basic"], rulesData.privs["admin"],
   371  			)
   372  			return testDefaultRules(ctx, expectedOutput, rulesEngine, t)
   373  		}).
   374  		Feature()
   375  
   376  	// Run the tests
   377  	f.Test(t, feat)
   378  }
   379  
   380  const rulesOverviewData = `
   381  INSERT INTO ea_rules_commands (command_id, name)
   382  VALUES
   383  	('78587bb1-6ca2-4d2d-a223-1ee642514b97', 'ls')
   384  ;
   385  
   386  INSERT INTO banners (banner_edge_id, banner_name)
   387  VALUES
   388  	('2f9f5965-ed2a-4262-9fd9-9d2d8f8bee8a', 'myBanner')
   389  ;
   390  
   391  INSERT INTO ea_rules_privileges (privilege_id, name)
   392  VALUES
   393  	('a7c379ea-6e34-4017-8e86-eb545d7856a3', 'ea-read'),
   394  	('caedabee-ea7a-4421-a608-ec04106e61da', 'ea-write')
   395  ;`
   396  
   397  func TestReadAllRulesForCommand(t *testing.T) {
   398  	var (
   399  		rulesEngine *gin.Engine
   400  		buf         *bytes.Buffer
   401  	)
   402  
   403  	feat := f2.NewFeature("Admin Privilege Rules").
   404  		Setup("Create Rules Engine server", func(ctx f2.Context, t *testing.T) f2.Context {
   405  			var db = postgres.FromContextT(ctx, t).DB()
   406  			rulesEngine, buf = setupRulesEngine(t, db)
   407  			_ = buf
   408  			return ctx
   409  		}).
   410  		Setup("Add some data", func(ctx f2.Context, t *testing.T) f2.Context {
   411  			var (
   412  				db = postgres.FromContextT(ctx, t).DB()
   413  			)
   414  
   415  			_, err := db.ExecContext(ctx, rulesOverviewData)
   416  			require.NoError(t, err)
   417  
   418  			return ctx
   419  		}).Test("Read No Existing Rules", func(ctx f2.Context, t *testing.T) f2.Context {
   420  		// test the no existing rules for same command before adding any data to the test database
   421  		test := testCase{
   422  			url:            "/admin/rules/commands/ls",
   423  			method:         http.MethodGet,
   424  			expectedStatus: http.StatusOK,
   425  			expectedOut:    `null`,
   426  		}
   427  		ctx = testEndpoint(ctx, t, rulesEngine, test)
   428  		return ctx
   429  	}).Test("Read no existing default rules", func(ctx f2.Context, t *testing.T) f2.Context {
   430  		var db = postgres.FromContextT(ctx, t).DB()
   431  		// now test a default rule and make sure the command is still returned even if no default rules are present
   432  		_, err := db.ExecContext(ctx, `
   433  			INSERT INTO ea_rules (banner_edge_id, command_id, privilege_id)
   434  			VALUES
   435  				('2f9f5965-ed2a-4262-9fd9-9d2d8f8bee8a', '78587bb1-6ca2-4d2d-a223-1ee642514b97', 'a7c379ea-6e34-4017-8e86-eb545d7856a3')
   436  			;`)
   437  		require.NoError(t, err)
   438  		test := testCase{
   439  			url:            "/admin/rules/commands/ls",
   440  			method:         http.MethodGet,
   441  			expectedStatus: http.StatusOK,
   442  			expectedOut: `{
   443  					"command": {
   444  						"id": "78587bb1-6ca2-4d2d-a223-1ee642514b97",
   445  						"name": "ls"
   446  					},
   447  					"default": {},
   448  					"banners": [
   449  						{
   450  							"banner": {
   451  								"id": "2f9f5965-ed2a-4262-9fd9-9d2d8f8bee8a",
   452  								"name": "myBanner"
   453  							},
   454  							"privileges": [
   455  								{
   456  									"id": "a7c379ea-6e34-4017-8e86-eb545d7856a3",
   457  									"name": "ea-read"
   458  								}
   459  							]
   460  						}
   461  					]
   462  				}`,
   463  		}
   464  		ctx = testEndpoint(ctx, t, rulesEngine, test)
   465  		return ctx
   466  	}).Test("Read All Rules", func(ctx f2.Context, t *testing.T) f2.Context {
   467  		var db = postgres.FromContextT(ctx, t).DB()
   468  		// add banner rule for banner "myBanner" ls [ea-read] and check both default and banner are returned
   469  		_, err := db.ExecContext(ctx, `
   470  			INSERT INTO ea_rules_default (command_id, privilege_id)
   471  			VALUES
   472  				('78587bb1-6ca2-4d2d-a223-1ee642514b97', 'caedabee-ea7a-4421-a608-ec04106e61da')
   473  			;`,
   474  		)
   475  		require.NoError(t, err)
   476  		test := testCase{
   477  			url:            "/admin/rules/commands/ls",
   478  			method:         http.MethodGet,
   479  			expectedStatus: http.StatusOK,
   480  			expectedOut: `{
   481  				"command": {
   482  					"id": "78587bb1-6ca2-4d2d-a223-1ee642514b97",
   483  					"name": "ls"
   484  				},
   485  				"default": {
   486  					"privileges": [
   487  						{
   488  							"id": "caedabee-ea7a-4421-a608-ec04106e61da",
   489  							"name": "ea-write"
   490  						}
   491  					]
   492  				},
   493  				"banners": [
   494  					{
   495  						"banner": {
   496  							"id": "2f9f5965-ed2a-4262-9fd9-9d2d8f8bee8a",
   497  							"name": "myBanner"
   498  						},
   499  						"privileges": [
   500  							{
   501  								"id": "a7c379ea-6e34-4017-8e86-eb545d7856a3",
   502  								"name": "ea-read"
   503  							}
   504  						]
   505  					}
   506  				]
   507  			}`,
   508  		}
   509  		return testEndpoint(ctx, t, rulesEngine, test)
   510  	}).Feature()
   511  	f.Test(t, feat)
   512  }
   513  
   514  func testDefaultRules(ctx f2.Context, expectedOut string, rulesEngine *gin.Engine, t *testing.T) f2.Context {
   515  	test := testCase{
   516  		url:            "/admin/rules/default/commands",
   517  		method:         http.MethodGet,
   518  		expectedStatus: http.StatusOK,
   519  		expectedOut:    expectedOut,
   520  	}
   521  	return testEndpoint(ctx, t, rulesEngine, test)
   522  }
   523  
   524  type testCase struct {
   525  	url            string
   526  	method         string
   527  	body           io.Reader
   528  	expectedStatus int
   529  	// Expected returned data. If nil or an empty string asserts the result is
   530  	// empty. If a non-empty string does a json equality on the passed in string
   531  	// and the return data. If a function obeying the signature
   532  	// func(object interface{}, msgAndArgs ...interface{}) bool
   533  	// executes the function with the returned data as object
   534  	expectedOut any
   535  }
   536  
   537  func testEndpoint(ctx f2.Context, t *testing.T, rulesEngine *gin.Engine, test testCase) f2.Context {
   538  	t.Helper()
   539  
   540  	r := httptest.NewRecorder()
   541  	c, cancel := context.WithTimeout(ctx, 10*time.Second)
   542  	defer cancel()
   543  	req, err := http.NewRequestWithContext(c, test.method, test.url, test.body)
   544  	assert.NoError(t, err)
   545  
   546  	rulesEngine.ServeHTTP(r, req)
   547  
   548  	resp := r.Result()
   549  
   550  	assert.Equal(t, test.expectedStatus, resp.StatusCode)
   551  
   552  	switch assertion := test.expectedOut.(type) {
   553  	case string:
   554  		if assertion != "" {
   555  			assert.JSONEq(t, assertion, r.Body.String())
   556  		} else {
   557  			assert.Empty(t, r.Body.String())
   558  		}
   559  	case func(object interface{}, msgAndArgs ...interface{}) bool:
   560  		assertion(r.Body.String())
   561  	case nil:
   562  		assert.Empty(t, r.Body.String())
   563  	default:
   564  		assert.Fail(t, "expected string or assertion function")
   565  	}
   566  
   567  	return ctx
   568  }
   569  
   570  const schema = `
   571  CREATE OR REPLACE FUNCTION trigger_set_timestamp()
   572      RETURNS TRIGGER AS
   573  $$
   574  BEGIN
   575      NEW.updated_at = NOW();
   576      RETURN NEW;
   577  END;
   578  $$ LANGUAGE plpgsql;
   579  
   580  CREATE TABLE IF NOT EXISTS banners (
   581      banner_edge_id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
   582      banner_name text UNIQUE NOT NULL,
   583      created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
   584      updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
   585  );
   586  
   587  CREATE TABLE IF NOT EXISTS ea_rules_commands(
   588    command_id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
   589    name text NOT NULL,
   590    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
   591    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
   592  );
   593  
   594  CREATE TABLE IF NOT EXISTS ea_rules_privileges(
   595    privilege_id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
   596    name text NOT NULL,
   597    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
   598    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
   599  );
   600  
   601  CREATE TABLE IF NOT EXISTS ea_rules(
   602    banner_edge_id UUID NOT NULL,
   603    command_id UUID NOT NULL,
   604    privilege_id UUID NOT NULL,
   605    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
   606    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
   607    PRIMARY KEY (banner_edge_id, command_id, privilege_id),
   608    FOREIGN KEY (banner_edge_id) REFERENCES banners (banner_edge_id) ON DELETE CASCADE,
   609    FOREIGN KEY (command_id) REFERENCES ea_rules_commands (command_id),
   610    FOREIGN KEY (privilege_id) REFERENCES ea_rules_privileges (privilege_id)
   611  );
   612  
   613  CREATE TABLE IF NOT EXISTS ea_rules_default(
   614    command_id UUID NOT NULL,
   615    privilege_id UUID NOT NULL,
   616    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
   617    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
   618    PRIMARY KEY (command_id, privilege_id),
   619    FOREIGN KEY (command_id) REFERENCES ea_rules_commands (command_id),
   620    FOREIGN KEY (privilege_id) REFERENCES ea_rules_privileges (privilege_id)
   621  );
   622  
   623  CREATE
   624  OR
   625  REPLACE
   626  TRIGGER ea_rules_commands_timestamp
   627  BEFORE
   628  UPDATE ON ea_rules_commands
   629      FOR EACH ROW
   630  EXECUTE PROCEDURE trigger_set_timestamp();
   631  
   632  CREATE
   633  OR
   634  REPLACE
   635  TRIGGER ea_rules_privileges_timestamp
   636  BEFORE
   637  UPDATE ON ea_rules_privileges
   638      FOR EACH ROW
   639  EXECUTE PROCEDURE trigger_set_timestamp();
   640  
   641  CREATE
   642  OR
   643  REPLACE
   644  TRIGGER ea_rules_default_timestamp
   645  BEFORE
   646  UPDATE ON ea_rules_default
   647      FOR EACH ROW
   648  EXECUTE PROCEDURE trigger_set_timestamp();
   649  
   650  CREATE
   651  OR
   652  REPLACE
   653  TRIGGER ea_rules_timestamp
   654  BEFORE
   655  UPDATE ON ea_rules
   656      FOR EACH ROW
   657  EXECUTE PROCEDURE trigger_set_timestamp();
   658  
   659  ALTER TABLE ea_rules_commands ADD CONSTRAINT command_name_uniq UNIQUE (name);
   660  ALTER TABLE ea_rules_privileges ADD CONSTRAINT privilege_name_uniq UNIQUE (name);
   661  
   662  DO $$
   663      BEGIN
   664          BEGIN
   665              DROP TYPE IF EXISTS command_type;
   666  			CREATE TYPE command_type AS ENUM ('command', 'script');
   667          EXCEPTION
   668              WHEN dependent_objects_still_exist THEN RAISE NOTICE 'command types already exists';
   669          END;
   670      END;
   671  $$;
   672  
   673  ALTER TABLE ea_rules_commands 
   674      ADD COLUMN IF NOT EXISTS type command_type NOT NULL DEFAULT 'command',
   675      DROP CONSTRAINT IF EXISTS command_name_uniq,
   676      DROP CONSTRAINT IF EXISTS command_name_type_uniq,
   677      ADD CONSTRAINT command_name_type_uniq UNIQUE (name, type)
   678  ;
   679  
   680  DO $$
   681      BEGIN
   682          ALTER TYPE command_type RENAME VALUE 'script' TO 'executable';
   683      EXCEPTION
   684          WHEN invalid_parameter_value THEN RAISE notice 'script command_type does not exist';
   685      END;
   686  $$
   687  ;
   688  `
   689  
   690  const (
   691  	// uuid constants as
   692  	uuid1           = "78587bb1-6ca2-4d2d-a223-1ee642514b97"
   693  	uuid2           = "35cc70eb-689d-49d4-8bd8-fa1cb8b0928f"
   694  	uuid3           = "79bf815d-8e64-4b01-b12e-1f173a322766"
   695  	uuid4           = "113f6c32-5501-44ba-9cd5-76530be5aa67"
   696  	priviledgesData = `
   697  	INSERT INTO ea_rules_privileges (privilege_id, name)
   698  	VALUES
   699  		('78587bb1-6ca2-4d2d-a223-1ee642514b97', 'read')
   700  	;
   701  	`
   702  )
   703  
   704  type postRulesTestData struct {
   705  	privs   map[string]string // name and id for each privilege
   706  	comms   map[string]string // name and id for each command
   707  	privExs []string          // db executes for privilege table
   708  	commExs []string          // db executes for commands table
   709  }
   710  
   711  func newRulesData(privs, comms map[string]string) postRulesTestData {
   712  	rd := postRulesTestData{privs: privs, comms: comms}
   713  	rd.commExs = seedData("ea_rules_commands", comms)
   714  	rd.privExs = seedData("ea_rules_privileges", privs)
   715  	return rd
   716  }
   717  
   718  var (
   719  	rulesData = newRulesData(map[string]string{
   720  		"read":  uuid1,
   721  		"basic": uuid2,
   722  		"write": uuid3,
   723  		"admin": uuid4,
   724  	},
   725  		map[string]string{
   726  			"ls":   uuid1,
   727  			"echo": uuid2,
   728  		},
   729  	)
   730  )
   731  
   732  // generates a list of executes for the database for default rules endpoint tests
   733  func seedData(table string, data map[string]string) []string {
   734  	query := `INSERT INTO %s (%s, name)
   735  	VALUES
   736  		('%s','%s')
   737  	;`
   738  	idtype := ""
   739  	if table == "ea_rules_commands" {
   740  		idtype = "command_id"
   741  	} else {
   742  		idtype = "privilege_id"
   743  	}
   744  	res := []string{}
   745  	for name, id := range data {
   746  		res = append(res, fmt.Sprintf(query, table, idtype, id, name))
   747  	}
   748  	return res
   749  }
   750  

View as plain text