...

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

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

     1  package rulestest
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"net/http"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/gin-gonic/gin"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"edge-infra.dev/pkg/lib/fog"
    14  	rulesengine "edge-infra.dev/pkg/sds/emergencyaccess/rules"
    15  	"edge-infra.dev/pkg/sds/emergencyaccess/rules/server"
    16  	"edge-infra.dev/pkg/sds/emergencyaccess/rules/storage/file"
    17  	"edge-infra.dev/test/f2"
    18  	"edge-infra.dev/test/f2/x/postgres"
    19  )
    20  
    21  func setupRulesEngineNoDB(t *testing.T, dir string) (*gin.Engine, *bytes.Buffer) {
    22  	gin.SetMode(gin.TestMode)
    23  	router := gin.New()
    24  	buf := new(bytes.Buffer)
    25  	log := fog.New(fog.To(buf))
    26  
    27  	ds, err := file.New(log, dir)
    28  	require.NoError(t, err)
    29  
    30  	re := rulesengine.New(ds)
    31  	res, err := server.New(router, re, log)
    32  	require.NoError(t, err)
    33  
    34  	return res.GinEngine, buf
    35  }
    36  
    37  func TestValidateNoDB(t *testing.T) {
    38  	var (
    39  		testJSONDataDir = createTestDataDir(t, testdata)
    40  		reng            *gin.Engine
    41  	)
    42  
    43  	feat := f2.NewFeature("Validate no DB").
    44  		Setup("Setup Rules Engine No DB", func(ctx f2.Context, t *testing.T) f2.Context {
    45  			reng, _ = setupRulesEngineNoDB(t, testJSONDataDir)
    46  			return ctx
    47  		}).
    48  		Test("Test Validate Command Default Rule", validateDefaultRuleCommand(&reng)).
    49  		Test("Test Validate Command Banner Rule", validateBannerRuleCommand(&reng)).
    50  		Test("Test Validate Command Wrong role", validateCommandWrongRole(&reng)).
    51  		Test("Test Validate Command Not Listed", validateCommandNotListed(&reng)).
    52  		Test("Test Validate Command Wrong Banner", validateCommandWrongBanner(&reng)).
    53  		Feature()
    54  	f.Test(t, feat)
    55  }
    56  
    57  func TestValidateWithDB(t *testing.T) {
    58  	var (
    59  		reng *gin.Engine
    60  		buf  *bytes.Buffer
    61  	)
    62  
    63  	feat := f2.NewFeature("Validation with DB").
    64  		Setup("Create Rules Engine server", func(ctx f2.Context, t *testing.T) f2.Context {
    65  			var db = postgres.FromContextT(ctx, t).DB()
    66  			reng, buf = setupRulesEngine(t, db)
    67  			return ctx
    68  		}).
    69  		Setup("Add some data", func(ctx f2.Context, t *testing.T) f2.Context {
    70  			var (
    71  				db = postgres.FromContextT(ctx, t).DB()
    72  			)
    73  			_, err := db.ExecContext(ctx, validationData)
    74  			require.NoError(t, err)
    75  			return ctx
    76  		}).
    77  		Test("Test Validate Command Default Rule", validateDefaultRuleCommand(&reng)).
    78  		Test("Test Validate Command Banner Rule", validateBannerRuleCommand(&reng)).
    79  		Test("Test Validate Command Wrong role", validateCommandWrongRole(&reng)).
    80  		Test("Test Validate Command Not Listed", validateCommandNotListed(&reng)).
    81  		Test("Test Validate Command Wrong Banner", validateCommandWrongBanner(&reng)).
    82  		Test("Test Validate Command No Rule", validateCommandNoRuleDB(&reng)).
    83  		Test("Test Validate Executable Default Rule", validateDefaultRuleExecutable(&reng)).
    84  		Test("Test Validate Unknown Request Type", validateUnknownCommandType(&reng)).
    85  		Feature()
    86  	f.Test(t, feat)
    87  	fmt.Println(buf)
    88  }
    89  
    90  const validationData = `
    91  INSERT INTO ea_rules_commands (command_id, name, type)
    92  VALUES
    93  	('78587bb1-6ca2-4d2d-a223-1ee642514b97', 'ls', 'command'),
    94  	('c818a4cd-225d-4f60-9382-f96348da7af0', 'myScript', 'executable'),
    95  	('35cc70eb-689d-49d4-8bd8-fa1cb8b0928f','mv', 'command')
    96  ;
    97  
    98  INSERT INTO banners (banner_edge_id, banner_name)
    99  VALUES
   100  	('2f9f5965-ed2a-4262-9fd9-9d2d8f8bee8a', 'myBanner')
   101  ;
   102  
   103  INSERT INTO ea_rules_privileges (privilege_id, name)
   104  VALUES
   105  	('a7c379ea-6e34-4017-8e86-eb545d7856a3', 'ea-read'),
   106  	('caedabee-ea7a-4421-a608-ec04106e61da', 'ea-write')
   107  ;
   108  
   109  INSERT INTO ea_rules_default (command_id, privilege_id)
   110  VALUES
   111  	('78587bb1-6ca2-4d2d-a223-1ee642514b97', 'a7c379ea-6e34-4017-8e86-eb545d7856a3'),
   112  	('c818a4cd-225d-4f60-9382-f96348da7af0', 'a7c379ea-6e34-4017-8e86-eb545d7856a3')
   113  ;
   114  
   115  INSERT INTO ea_rules (banner_edge_id, command_id, privilege_id)
   116  VALUES
   117  	('2f9f5965-ed2a-4262-9fd9-9d2d8f8bee8a', '78587bb1-6ca2-4d2d-a223-1ee642514b97', 'caedabee-ea7a-4421-a608-ec04106e61da')
   118  ;
   119  `
   120  
   121  func validateDefaultRuleCommand(reng **gin.Engine) func(ctx f2.Context, t *testing.T) f2.Context {
   122  	return func(ctx f2.Context, t *testing.T) f2.Context {
   123  		tc := testCase{
   124  			url:    `/validatecommand`,
   125  			method: http.MethodPost,
   126  			body: strings.NewReader(`{
   127  				"command": {
   128  					"name": "ls",
   129  					"type": "command"
   130  				},
   131  				"identity":{"userid":"user@ncr.com","earoles":["ea-read"]}, 
   132  				"target":{"bannerID":"2f9f5965-ed2a-4262-9fd9-9d2d8f8bee8a"}}`),
   133  			expectedStatus: http.StatusOK,
   134  			expectedOut: `{
   135  				"valid":true
   136  			}`,
   137  		}
   138  		ctx = testEndpoint(ctx, t, *reng, tc)
   139  		return ctx
   140  	}
   141  }
   142  func validateBannerRuleCommand(reng **gin.Engine) func(ctx f2.Context, t *testing.T) f2.Context {
   143  	return func(ctx f2.Context, t *testing.T) f2.Context {
   144  		tc := testCase{
   145  			url:    `/validatecommand`,
   146  			method: http.MethodPost,
   147  			body: strings.NewReader(`{
   148  				"command": {
   149  					"name": "ls",
   150  					"type": "command"
   151  				},
   152  				"identity":{"userid":"user@ncr.com","earoles":["ea-write"]}, 
   153  				"target":{"bannerID":"2f9f5965-ed2a-4262-9fd9-9d2d8f8bee8a"}}`),
   154  			expectedStatus: http.StatusOK,
   155  			expectedOut: `{
   156  				"valid":true
   157  			}`,
   158  		}
   159  		ctx = testEndpoint(ctx, t, *reng, tc)
   160  		return ctx
   161  	}
   162  }
   163  func validateCommandWrongRole(reng **gin.Engine) func(ctx f2.Context, t *testing.T) f2.Context {
   164  	return func(ctx f2.Context, t *testing.T) f2.Context {
   165  		tc := testCase{
   166  			url:    `/validatecommand`,
   167  			method: http.MethodPost,
   168  			body: strings.NewReader(`{
   169  				"command": {
   170  					"name": "ls",
   171  					"type": "command"
   172  				},
   173  				"identity":{"userid":"user@ncr.com","earoles":["not-a-role"]}, 
   174  				"target":{"bannerID":"2f9f5965-ed2a-4262-9fd9-9d2d8f8bee8a"}}`),
   175  			expectedStatus: http.StatusOK,
   176  			expectedOut: `{
   177  				"valid":false
   178  			}`,
   179  		}
   180  		ctx = testEndpoint(ctx, t, *reng, tc)
   181  		return ctx
   182  	}
   183  }
   184  func validateCommandNotListed(reng **gin.Engine) func(ctx f2.Context, t *testing.T) f2.Context {
   185  	return func(ctx f2.Context, t *testing.T) f2.Context {
   186  		tc := testCase{
   187  			url:    `/validatecommand`,
   188  			method: http.MethodPost,
   189  			// mkdir is not listed anywhere so will fail
   190  			body: strings.NewReader(`{
   191  				"command": {
   192  					"name": "mkdir",
   193  					"type": "command"
   194  				},
   195  				"identity":{"userid":"user@ncr.com","earoles":["ea-write"]}, 
   196  				"target":{"bannerID":"2f9f5965-ed2a-4262-9fd9-9d2d8f8bee8a"}}`),
   197  			expectedStatus: http.StatusOK,
   198  			expectedOut: `{
   199  				"valid":false
   200  			}`,
   201  		}
   202  		ctx = testEndpoint(ctx, t, *reng, tc)
   203  		return ctx
   204  	}
   205  }
   206  func validateCommandWrongBanner(reng **gin.Engine) func(ctx f2.Context, t *testing.T) f2.Context {
   207  	return func(ctx f2.Context, t *testing.T) f2.Context {
   208  		tc := testCase{
   209  			url:    `/validatecommand`,
   210  			method: http.MethodPost,
   211  			// this banner ID doesnt exist in the ds so only default roles will be returned
   212  			body: strings.NewReader(`{
   213  				"command": {
   214  					"name": "mv",
   215  					"type": "command"
   216  				},
   217  				"identity":{"userid":"user@ncr.com","earoles":["ea-read"]}, 
   218  				"target":{"bannerID":"35cc70eb-689d-49d4-8bd8-fa1cb8b0928f"}}`),
   219  			expectedStatus: http.StatusOK,
   220  			expectedOut: `{
   221  				"valid":false
   222  			}`,
   223  		}
   224  		ctx = testEndpoint(ctx, t, *reng, tc)
   225  		return ctx
   226  	}
   227  }
   228  func validateCommandNoRuleDB(reng **gin.Engine) func(ctx f2.Context, t *testing.T) f2.Context {
   229  	return func(ctx f2.Context, t *testing.T) f2.Context {
   230  		tc := testCase{
   231  			url:    `/validatecommand`,
   232  			method: http.MethodPost,
   233  			// this command exists in the DB but doesnt have a role associated to it
   234  			body: strings.NewReader(`{
   235  				"command": {
   236  					"name": "mv",
   237  					"type": "command"
   238  				},
   239  				"identity":{"userid":"user@ncr.com","earoles":["ea-read"]}, 
   240  				"target":{"bannerID":"2f9f5965-ed2a-4262-9fd9-9d2d8f8bee8a"}}`),
   241  			expectedStatus: http.StatusOK,
   242  			expectedOut: `{
   243  				"valid":false
   244  			}`,
   245  		}
   246  		ctx = testEndpoint(ctx, t, *reng, tc)
   247  		return ctx
   248  	}
   249  }
   250  
   251  func validateDefaultRuleExecutable(reng **gin.Engine) func(ctx f2.Context, t *testing.T) f2.Context {
   252  	return func(ctx f2.Context, t *testing.T) f2.Context {
   253  		tc := testCase{
   254  			url:    `/validatecommand`,
   255  			method: http.MethodPost,
   256  			body: strings.NewReader(`
   257  				{
   258  					"command": {
   259  						"name": "myScript",
   260  						"type": "executable"
   261  					},
   262  					"identity": {
   263  						"userid": "user@ncr.com",
   264  						"earoles":["ea-read"]
   265  					},
   266  					"target": {
   267  						"bannerID": "2f9f5965-ed2a-4262-9fd9-9d2d8f8bee8a"
   268  					}
   269  				}
   270  			`),
   271  			expectedStatus: http.StatusOK,
   272  			expectedOut: `
   273  				{
   274  					"valid": true
   275  				}
   276  			`,
   277  		}
   278  		ctx = testEndpoint(ctx, t, *reng, tc)
   279  		return ctx
   280  	}
   281  }
   282  
   283  func validateUnknownCommandType(reng **gin.Engine) func(ctx f2.Context, t *testing.T) f2.Context {
   284  	return func(ctx f2.Context, t *testing.T) f2.Context {
   285  		tc := testCase{
   286  			url:    `/validatecommand`,
   287  			method: http.MethodPost,
   288  			body: strings.NewReader(`
   289  				{
   290  					"command": {
   291  						"name": "myScript",
   292  						"type": "unknownRequestType"
   293  					},
   294  					"identity": {
   295  						"userid": "user@ncr.com",
   296  						"earoles":["ea-read"]
   297  					},
   298  					"target": {
   299  						"bannerID": "2f9f5965-ed2a-4262-9fd9-9d2d8f8bee8a"
   300  					}
   301  				}
   302  			`),
   303  			// Currently we expect this to fail with an Internal Server Error.
   304  			// In future we may want to update this to be an BadRequest, however
   305  			// as this api is currently only exposed to authservice which
   306  			// validates the request type, an unknown request type here
   307  			// indicates a bug that must be fixed in authservice, so 500 is
   308  			// acceptable for now.
   309  			expectedStatus: http.StatusInternalServerError,
   310  			expectedOut:    `null`,
   311  		}
   312  		ctx = testEndpoint(ctx, t, *reng, tc)
   313  		return ctx
   314  	}
   315  }
   316  

View as plain text