github.com/greenpau/go-authcrunch@v1.1.4/pkg/ids/local/store_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 local
    16  
    17  import (
    18  	"fmt"
    19  	"path"
    20  	"path/filepath"
    21  	"testing"
    22  
    23  	"github.com/greenpau/go-authcrunch/internal/tests"
    24  	"github.com/greenpau/go-authcrunch/internal/testutils"
    25  	"github.com/greenpau/go-authcrunch/pkg/authn/enums/operator"
    26  	"github.com/greenpau/go-authcrunch/pkg/errors"
    27  	"github.com/greenpau/go-authcrunch/pkg/requests"
    28  	logutil "github.com/greenpau/go-authcrunch/pkg/util/log"
    29  	"go.uber.org/zap"
    30  )
    31  
    32  func TestNewIdentityStore(t *testing.T) {
    33  	db, err := testutils.CreateTestDatabase("TestLocalIdentityStore")
    34  	if err != nil {
    35  		t.Fatalf("failed to create temp dir: %v", err)
    36  	}
    37  	dbPath := db.GetPath()
    38  	testcases := []struct {
    39  		name              string
    40  		config            *Config
    41  		logger            *zap.Logger
    42  		testRequests      bool
    43  		publicKeysEnabled bool
    44  		want              map[string]interface{}
    45  		shouldErr         bool
    46  		err               error
    47  	}{
    48  		{
    49  			name: "test local store",
    50  			config: &Config{
    51  				Name:  "local_store",
    52  				Realm: "local",
    53  				Path:  dbPath,
    54  			},
    55  			logger: logutil.NewLogger(),
    56  			want: map[string]interface{}{
    57  				"name":  "local_store",
    58  				"kind":  "local",
    59  				"realm": "local",
    60  				"config": map[string]interface{}{
    61  					"name":  "local_store",
    62  					"realm": "local",
    63  					"path":  dbPath,
    64  					"login_icon": map[string]interface{}{
    65  						"background_color": string("#324960"),
    66  						"class_name":       string("las la-key la-2x"),
    67  						"color":            string("white"),
    68  						"text_color":       string("#37474f"),
    69  					},
    70  				},
    71  			},
    72  		},
    73  		{
    74  			name: "test local store with operations",
    75  			config: &Config{
    76  				Name:  "local_store",
    77  				Realm: "local",
    78  				Path:  dbPath,
    79  			},
    80  			logger:            logutil.NewLogger(),
    81  			testRequests:      true,
    82  			publicKeysEnabled: true,
    83  			want: map[string]interface{}{
    84  				"name":  "local_store",
    85  				"realm": "local",
    86  				"kind":  "local",
    87  				"config": map[string]interface{}{
    88  					"name":  "local_store",
    89  					"realm": "local",
    90  					"path":  dbPath,
    91  					"login_icon": map[string]interface{}{
    92  						"background_color": string("#324960"),
    93  						"class_name":       string("las la-key la-2x"),
    94  						"color":            string("white"),
    95  						"text_color":       string("#37474f"),
    96  					},
    97  				},
    98  				"ops": map[string]bool{
    99  					"AddAPIKey":       true,
   100  					"AddKeyGPG":       true,
   101  					"AddKeySSH":       true,
   102  					"AddMfaToken":     true,
   103  					"AddUser":         true,
   104  					"ChangePassword":  true,
   105  					"DeleteAPIKey":    true,
   106  					"DeleteMfaToken":  true,
   107  					"DeletePublicKey": true,
   108  					"DeleteUser":      true,
   109  					"GetAPIKeys":      false,
   110  					"GetMfaTokens":    false,
   111  					"GetMfaToken":     true,
   112  					"GetPublicKeys":   false,
   113  					"GetUser":         false,
   114  					"GetUsers":        false,
   115  					"IdentifyUser":    false,
   116  					"LookupAPIKey":    true,
   117  				},
   118  			},
   119  		},
   120  		{
   121  			name: "test empty config name",
   122  			config: &Config{
   123  				Name:  "",
   124  				Realm: "local",
   125  				Path:  filepath.Join(path.Dir(dbPath), "user_db1.json"),
   126  			},
   127  			logger:    logutil.NewLogger(),
   128  			shouldErr: true,
   129  			err:       errors.ErrIdentityStoreConfigureNameEmpty,
   130  		},
   131  		{
   132  			name: "test empty config realm",
   133  			config: &Config{
   134  				Name: "local_store",
   135  				Path: filepath.Join(path.Dir(dbPath), "user_db1.json"),
   136  			},
   137  			logger:    logutil.NewLogger(),
   138  			shouldErr: true,
   139  			err:       errors.ErrIdentityStoreConfigureRealmEmpty,
   140  		},
   141  		{
   142  			name: "test empty config database path",
   143  			config: &Config{
   144  				Name:  "local_store",
   145  				Realm: "local",
   146  			},
   147  			logger:    logutil.NewLogger(),
   148  			shouldErr: true,
   149  			err:       errors.ErrIdentityStoreLocalConfigurePathEmpty,
   150  		},
   151  		{
   152  			name: "test empty logger",
   153  			config: &Config{
   154  				Name:  "local_store",
   155  				Realm: "local",
   156  				Path:  filepath.Join(path.Dir(dbPath), "user_db1.json"),
   157  			},
   158  			shouldErr: true,
   159  			err:       errors.ErrIdentityStoreConfigureLoggerNotFound,
   160  		},
   161  	}
   162  	for _, tc := range testcases {
   163  		t.Run(tc.name, func(t *testing.T) {
   164  			got := make(map[string]interface{})
   165  			msgs := []string{fmt.Sprintf("test name: %s", tc.name)}
   166  			msgs = append(msgs, fmt.Sprintf("db path: %v", tc.config.Path))
   167  			msgs = append(msgs, fmt.Sprintf("config:\n%v", tc.config))
   168  
   169  			st, err := NewIdentityStore(tc.config, tc.logger)
   170  			if tests.EvalErrWithLog(t, err, "NewIdentityStore", tc.shouldErr, tc.err, msgs) {
   171  				return
   172  			}
   173  
   174  			if tc.testRequests {
   175  				results := make(map[string]bool)
   176  				if err := st.Configure(); err != nil {
   177  					t.Fatalf("configuration error: %v", err)
   178  				}
   179  
   180  				ops := []operator.Type{
   181  					operator.ChangePassword,
   182  					operator.AddKeySSH,
   183  					operator.AddKeyGPG,
   184  					operator.GetPublicKeys,
   185  					operator.DeletePublicKey,
   186  					operator.AddMfaToken,
   187  					operator.GetMfaTokens,
   188  					operator.GetMfaToken,
   189  					operator.DeleteMfaToken,
   190  					operator.AddUser,
   191  					operator.GetUser,
   192  					operator.GetUsers,
   193  					operator.DeleteUser,
   194  					operator.IdentifyUser,
   195  					operator.AddAPIKey,
   196  					operator.DeleteAPIKey,
   197  					operator.GetAPIKeys,
   198  					operator.LookupAPIKey,
   199  				}
   200  
   201  				if tc.publicKeysEnabled {
   202  					ops = append(ops, operator.GetPublicKeys)
   203  				}
   204  
   205  				for _, op := range ops {
   206  					req := &requests.Request{
   207  						User: requests.User{
   208  							Username: tests.TestUser1,
   209  							Email:    tests.TestEmail1,
   210  						},
   211  					}
   212  					if op == operator.GetPublicKeys {
   213  						req.Key = requests.Key{
   214  							Usage: "ssh",
   215  						}
   216  					}
   217  
   218  					if err := st.Request(op, req); err != nil {
   219  						results[op.String()] = true
   220  					} else {
   221  						results[op.String()] = false
   222  					}
   223  				}
   224  
   225  				got["ops"] = results
   226  			}
   227  
   228  			got["name"] = st.GetName()
   229  			got["realm"] = st.GetRealm()
   230  			got["config"] = st.GetConfig()
   231  			got["kind"] = st.GetKind()
   232  
   233  			tests.EvalObjectsWithLog(t, "config", tc.want, got, msgs)
   234  		})
   235  	}
   236  }
   237  
   238  func TestConfigureIdentityStore(t *testing.T) {
   239  	db, err := testutils.CreateTestDatabase("TestLocalIdentityStore")
   240  	if err != nil {
   241  		t.Fatalf("failed to create temp dir: %v", err)
   242  	}
   243  	dbPath := db.GetPath()
   244  	testcases := []struct {
   245  		name           string
   246  		configs        []*Config
   247  		testRequests   bool
   248  		skipPublicKeys bool
   249  		want           map[string]interface{}
   250  		shouldErr      bool
   251  		err            error
   252  	}{
   253  		{
   254  			name: "test two configs having same realm and same path",
   255  			configs: []*Config{
   256  				&Config{
   257  					Name:  "local_store",
   258  					Realm: "local",
   259  					Path:  dbPath,
   260  				},
   261  				&Config{
   262  					Name:  "local_store",
   263  					Realm: "local",
   264  					Path:  dbPath,
   265  				},
   266  			},
   267  			want: map[string]interface{}{
   268  				"local_store_kind":  "local",
   269  				"local_store_realm": "local",
   270  				"local_store_config": map[string]interface{}{
   271  					"name":  "local_store",
   272  					"realm": "local",
   273  					"path":  dbPath,
   274  					"login_icon": map[string]interface{}{
   275  						"background_color": string("#324960"),
   276  						"class_name":       string("las la-key la-2x"),
   277  						"color":            string("white"),
   278  						"text_color":       string("#37474f"),
   279  					},
   280  				},
   281  				"local_store_configured": true,
   282  			},
   283  		},
   284  		{
   285  			name: "test unsupported file path",
   286  			configs: []*Config{
   287  				&Config{
   288  					Name:  "local_store",
   289  					Realm: "local",
   290  					Path:  "/dev/null",
   291  				},
   292  			},
   293  			shouldErr: true,
   294  			err:       errors.ErrNewDatabase.WithArgs("/dev/null", "null path"),
   295  		},
   296  	}
   297  	for _, tc := range testcases {
   298  		t.Run(tc.name, func(t *testing.T) {
   299  			got := make(map[string]interface{})
   300  			msgs := []string{fmt.Sprintf("test name: %s", tc.name)}
   301  			logger := logutil.NewLogger()
   302  
   303  			for i, config := range tc.configs {
   304  				msgs = append(msgs, fmt.Sprintf("db path %d: %v", i, config.Path))
   305  				msgs = append(msgs, fmt.Sprintf("config:\n%v", config))
   306  				b, err := NewIdentityStore(config, logger)
   307  				if err != nil {
   308  					t.Fatalf("failed creating identity store: %v", err)
   309  				}
   310  
   311  				err = b.Configure()
   312  				if i == 0 && len(tc.configs) > 1 {
   313  					if err != nil {
   314  						t.Fatalf("first config expected to succeed, got error: %v", err)
   315  					}
   316  				} else {
   317  					if tests.EvalErrWithLog(t, err, "Configure", tc.shouldErr, tc.err, msgs) {
   318  						return
   319  					}
   320  				}
   321  
   322  				got[b.GetName()+"_realm"] = b.GetRealm()
   323  				got[b.GetName()+"_kind"] = b.GetKind()
   324  				got[b.GetName()+"_config"] = b.GetConfig()
   325  				got[b.GetName()+"_configured"] = b.Configured()
   326  			}
   327  			tests.EvalObjectsWithLog(t, "config", tc.want, got, msgs)
   328  		})
   329  	}
   330  }