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