github.com/greenpau/go-authcrunch@v1.1.4/pkg/authn/transformer/transformer_test.go (about)

     1  // Copyright 2022 Paul Greenberg greenpau@outlook.com
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package transformer
    16  
    17  import (
    18  	"encoding/json"
    19  	"fmt"
    20  	"github.com/greenpau/go-authcrunch/internal/tests"
    21  	"github.com/greenpau/go-authcrunch/pkg/acl"
    22  	"testing"
    23  )
    24  
    25  func TestFactory(t *testing.T) {
    26  	var testcases = []struct {
    27  		name    string
    28  		configs []*Config
    29  		user    map[string]interface{}
    30  		keys    []string
    31  		// Expected results.
    32  		want      map[string]interface{}
    33  		shouldErr bool
    34  		err       error
    35  	}{
    36  		{
    37  			name: "add authp/admin role to greenpau@outlook.com",
    38  			user: map[string]interface{}{
    39  				"email": "greenpau@outlook.com",
    40  				"roles": "editor",
    41  			},
    42  			keys: []string{
    43  				"challenges",
    44  				"roles",
    45  			},
    46  			configs: []*Config{
    47  				{
    48  					Matchers: []string{
    49  						"exact match email greenpau@outlook.com",
    50  					},
    51  					Actions: []string{
    52  						"add role authp/admin authp/viewer",
    53  						"add role authp/editor",
    54  						"require mfa",
    55  					},
    56  				},
    57  			},
    58  			want: map[string]interface{}{
    59  				"roles": []string{
    60  					"editor",
    61  					"authp/admin",
    62  					"authp/viewer",
    63  					"authp/editor",
    64  				},
    65  				"challenges": []string{
    66  					"mfa",
    67  				},
    68  			},
    69  		},
    70  		{
    71  			name: "drop existing authp/viewer role",
    72  			user: map[string]interface{}{
    73  				"email": "greenpau@outlook.com",
    74  				"roles": []string{"authp/admin", "authp/editor", "authp/viewer"},
    75  			},
    76  			keys: []string{
    77  				"roles",
    78  			},
    79  			configs: []*Config{
    80  				{
    81  					Matchers: []string{
    82  						"regex match role viewer",
    83  					},
    84  					Actions: []string{
    85  						"action drop matched role",
    86  					},
    87  				},
    88  			},
    89  			want: map[string]interface{}{
    90  				"roles": []string{
    91  					"authp/admin",
    92  					"authp/editor",
    93  				},
    94  			},
    95  		},
    96  		{
    97  			name: "drop any role without words authp/admin or authp/user",
    98  			user: map[string]interface{}{
    99  				"email": "greenpau@outlook.com",
   100  				"roles": []string{
   101  					"authp/admin",
   102  					"authp/editor",
   103  					"authp/viewer",
   104  					"authp/user",
   105  				},
   106  			},
   107  			keys: []string{
   108  				"roles",
   109  			},
   110  			configs: []*Config{
   111  				{
   112  					Matchers: []string{
   113  						"no regex match any role ^authp/(admin|user)$",
   114  					},
   115  					Actions: []string{
   116  						"action drop matched role",
   117  					},
   118  				},
   119  			},
   120  			want: map[string]interface{}{
   121  				"roles": []string{
   122  					"authp/admin",
   123  					"authp/user",
   124  				},
   125  			},
   126  		},
   127  		{
   128  			name: "drop any role without words authp/admin or authp/user and no roles found",
   129  			user: map[string]interface{}{
   130  				"email": "greenpau@outlook.com",
   131  				"roles": []string{"authp/editor"},
   132  			},
   133  			keys: []string{
   134  				"roles",
   135  			},
   136  			configs: []*Config{
   137  				{
   138  					Matchers: []string{
   139  						"no regex match any role ^authp/(admin|user)$",
   140  					},
   141  					Actions: []string{
   142  						"action drop matched role",
   143  					},
   144  				},
   145  			},
   146  			want: map[string]interface{}{
   147  				"roles": []string(nil),
   148  			},
   149  		},
   150  	}
   151  	for _, tc := range testcases {
   152  		t.Run(tc.name, func(t *testing.T) {
   153  			msgs := []string{fmt.Sprintf("test name: %s", tc.name)}
   154  			tr, err := NewFactory(tc.configs)
   155  			if err != nil {
   156  				if tests.EvalErrWithLog(t, err, "transformer", tc.shouldErr, tc.err, msgs) {
   157  					return
   158  				}
   159  			}
   160  			if err := tr.Transform(tc.user); err != nil {
   161  				if tests.EvalErrWithLog(t, err, "transformer", tc.shouldErr, tc.err, msgs) {
   162  					return
   163  				}
   164  			}
   165  			got := make(map[string]interface{})
   166  			for _, k := range tc.keys {
   167  				if v, exists := tc.user[k]; exists {
   168  					got[k] = v
   169  				}
   170  			}
   171  			tests.EvalObjectsWithLog(t, "transformer", tc.want, got, msgs)
   172  		})
   173  	}
   174  }
   175  
   176  func TestTransformData(t *testing.T) {
   177  	var testcases = []struct {
   178  		name      string
   179  		args      []string
   180  		matcher   *acl.AccessList
   181  		user      map[string]interface{}
   182  		want      map[string]interface{}
   183  		shouldErr bool
   184  		err       error
   185  	}{
   186  		{
   187  			name: "add role authp/user with webadmin",
   188  			args: []string{"add", "role", "authp/user"},
   189  			user: map[string]interface{}{
   190  				"sub":   "webadmin",
   191  				"roles": []string{"authp/admin"},
   192  			},
   193  			want: map[string]interface{}{
   194  				"sub":   "webadmin",
   195  				"roles": []string{"authp/admin", "authp/user"},
   196  			},
   197  		},
   198  		{
   199  			name: "add add _couchdb.roles _admin with webadmin",
   200  			args: []string{"add", "_couchdb.roles", "_admin", "as", "string", "list"},
   201  			user: map[string]interface{}{
   202  				"sub":   "webadmin",
   203  				"roles": []string{"authp/admin", "authp/user"},
   204  			},
   205  			want: map[string]interface{}{
   206  				"sub":            "webadmin",
   207  				"roles":          []interface{}{string("authp/admin"), string("authp/user")},
   208  				"_couchdb.roles": []string{"_admin"},
   209  			},
   210  		},
   211  		{
   212  			name: "add add _couchdb.db _admin with webadmin",
   213  			args: []string{"add", "_couchdb.db", "accounts", "as", "string"},
   214  			user: map[string]interface{}{
   215  				"sub":   "webadmin",
   216  				"roles": []string{"authp/admin", "authp/user"},
   217  			},
   218  			want: map[string]interface{}{
   219  				"sub":         "webadmin",
   220  				"roles":       []interface{}{string("authp/admin"), string("authp/user")},
   221  				"_couchdb.db": "accounts",
   222  			},
   223  		},
   224  		{
   225  			name: "as type directive is too short",
   226  			args: []string{"add", "_couchdb.roles", "_admin", "as"},
   227  			user: map[string]interface{}{
   228  				"sub":   "webadmin",
   229  				"roles": []string{"authp/admin", "authp/user"},
   230  			},
   231  			shouldErr: true,
   232  			err: fmt.Errorf(
   233  				"failed transforming %q field for %q action in %v: %v",
   234  				"_couchdb.roles", "add", []string{"add", "_couchdb.roles", "_admin", "as"},
   235  				"as type directive is too short",
   236  			),
   237  		},
   238  		{
   239  			name: "unsupported data type",
   240  			args: []string{"add", "_couchdb.roles", "_admin", "as", "foo"},
   241  			user: map[string]interface{}{
   242  				"sub":   "webadmin",
   243  				"roles": []string{"authp/admin", "authp/user"},
   244  			},
   245  			shouldErr: true,
   246  			err: fmt.Errorf(
   247  				"failed transforming %q field for %q action in %v: %v",
   248  				"_couchdb.roles", "add", []string{"add", "_couchdb.roles", "_admin", "as", "foo"},
   249  				"unsupported \"foo\" data type",
   250  			),
   251  		},
   252  		{
   253  			name: "add matrix_id claim with replacer from sub claim",
   254  			args: []string{"add", "matrix_id", "@{claims.sub}:matrix.foo.bar", "as", "string"},
   255  			user: map[string]interface{}{
   256  				"sub":   "webadmin",
   257  				"roles": []string{"authp/admin"},
   258  			},
   259  			want: map[string]interface{}{
   260  				"sub":       "webadmin",
   261  				"roles":     []interface{}{"authp/admin"},
   262  				"matrix_id": "@webadmin:matrix.foo.bar",
   263  			},
   264  		},
   265  		{
   266  			name: "add matrix_id claim with replacer from sub and email claims",
   267  			args: []string{"add", "matrix_id", "@{claims.sub}:{claims.email}:matrix.foo.bar", "as", "string"},
   268  			user: map[string]interface{}{
   269  				"sub":   "webadmin",
   270  				"email": "webadmin@localdomain.local",
   271  				"roles": []string{"authp/admin"},
   272  			},
   273  			want: map[string]interface{}{
   274  				"sub":       "webadmin",
   275  				"roles":     []interface{}{"authp/admin"},
   276  				"email":     "webadmin@localdomain.local",
   277  				"matrix_id": "@webadmin:webadmin@localdomain.local:matrix.foo.bar",
   278  			},
   279  		},
   280  		{
   281  			name: "add roles based on replacer from realm claim",
   282  			args: []string{
   283  				"add", "roles", "{claims.realm}/admin", "{claims.realm}/user"},
   284  			user: map[string]interface{}{
   285  				"sub":   "webadmin",
   286  				"realm": "local",
   287  				"email": "webadmin@localdomain.local",
   288  				"roles": []string{"authp/admin"},
   289  			},
   290  			want: map[string]interface{}{
   291  				"sub":   "webadmin",
   292  				"realm": "local",
   293  				"roles": []string{"authp/admin", "local/admin", "local/user"},
   294  				"email": "webadmin@localdomain.local",
   295  			},
   296  		},
   297  		{
   298  			name: "add email claim based on replacer from sub and realm claims",
   299  			args: []string{
   300  				"add", "email", "{claims.sub}@{claims.realm}"},
   301  			user: map[string]interface{}{
   302  				"sub":   "webadmin",
   303  				"realm": "localdomain.local",
   304  				"roles": []string{"authp/admin"},
   305  			},
   306  			want: map[string]interface{}{
   307  				"sub":   "webadmin",
   308  				"realm": "localdomain.local",
   309  				"roles": []interface{}{"authp/admin"},
   310  				"email": "webadmin@localdomain.local",
   311  			},
   312  		},
   313  	}
   314  	for _, tc := range testcases {
   315  		t.Run(tc.name, func(t *testing.T) {
   316  			msgs := []string{fmt.Sprintf("test name: %s", tc.name)}
   317  			got := deepCopy(tc.user)
   318  			if err := transformData(tc.args, got, tc.matcher); err != nil {
   319  				if tests.EvalErrWithLog(t, err, "transformer", tc.shouldErr, tc.err, msgs) {
   320  					return
   321  				}
   322  			}
   323  			tests.EvalObjectsWithLog(t, "transformer", tc.want, got, msgs)
   324  		})
   325  	}
   326  }
   327  
   328  func deepCopy(src map[string]interface{}) map[string]interface{} {
   329  	if src == nil {
   330  		return nil
   331  	}
   332  	j, _ := json.Marshal(src)
   333  	m := make(map[string]interface{})
   334  	json.Unmarshal(j, &m)
   335  	return m
   336  }