eintopf.info@v0.13.16/service/user/authorizer_test.go (about)

     1  // Copyright (C) 2022 The Eintopf authors
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    15  
    16  package user_test
    17  
    18  import (
    19  	"context"
    20  	"log"
    21  	"testing"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	"github.com/google/go-cmp/cmp/cmpopts"
    25  
    26  	"eintopf.info/service/auth"
    27  	"eintopf.info/service/user"
    28  )
    29  
    30  var (
    31  	roleAdmin    = auth.RoleAdmin
    32  	roleModertor = auth.RoleModerator
    33  	roleNormal   = auth.RoleNormal
    34  	roleInternal = auth.RoleInternal
    35  )
    36  
    37  func TestAuthorizerCreate(t *testing.T) {
    38  	for _, test := range []struct {
    39  		name      string
    40  		userID    string
    41  		role      *auth.Role
    42  		user      *user.NewUser
    43  		wantError error
    44  	}{
    45  		{
    46  			name:      "UnauthorizedWithNoUserIDAndRole",
    47  			userID:    "",
    48  			role:      nil,
    49  			user:      &user.NewUser{},
    50  			wantError: auth.ErrUnauthorized,
    51  		}, {
    52  			name:      "Authorized:Internal->Admin",
    53  			userID:    "foo",
    54  			role:      &roleInternal,
    55  			user:      &user.NewUser{Role: auth.RoleAdmin, Password: "foo"},
    56  			wantError: nil,
    57  		}, {
    58  			name:      "Unauthorized:Admin->Internal",
    59  			userID:    "foo",
    60  			role:      &roleAdmin,
    61  			user:      &user.NewUser{Role: auth.RoleInternal, Password: "foo"},
    62  			wantError: auth.ErrUnauthorized,
    63  		}, {
    64  			name:      "Authorized:Admin->Admin",
    65  			userID:    "foo",
    66  			role:      &roleAdmin,
    67  			user:      &user.NewUser{Role: auth.RoleAdmin, Password: "foo"},
    68  			wantError: nil,
    69  		}, {
    70  			name:      "Unauthorized:Moderator->Internal",
    71  			userID:    "foo",
    72  			role:      &roleModertor,
    73  			user:      &user.NewUser{Role: auth.RoleInternal, Password: "foo"},
    74  			wantError: auth.ErrUnauthorized,
    75  		}, {
    76  			name:      "Unauthorized:Moderator->Admin",
    77  			userID:    "foo",
    78  			role:      &roleModertor,
    79  			user:      &user.NewUser{Role: auth.RoleAdmin, Password: "foo"},
    80  			wantError: auth.ErrUnauthorized,
    81  		}, {
    82  			name:      "Authorized:Moderator->Moderator",
    83  			userID:    "foo",
    84  			role:      &roleModertor,
    85  			user:      &user.NewUser{Role: auth.RoleModerator, Password: "foo"},
    86  			wantError: nil,
    87  		}, {
    88  			name:      "Unauthorized:Normal->Internal",
    89  			userID:    "foo",
    90  			role:      &roleNormal,
    91  			user:      &user.NewUser{Role: auth.RoleInternal, Password: "foo"},
    92  			wantError: auth.ErrUnauthorized,
    93  		}, {
    94  			name:      "Unauthorized:Normal->Admin",
    95  			userID:    "foo",
    96  			role:      &roleNormal,
    97  			user:      &user.NewUser{Role: auth.RoleAdmin, Password: "foo"},
    98  			wantError: auth.ErrUnauthorized,
    99  		}, {
   100  			name:      "Unauthorized:Normal->Moderator",
   101  			userID:    "foo",
   102  			role:      &roleNormal,
   103  			user:      &user.NewUser{Role: auth.RoleModerator, Password: "foo"},
   104  			wantError: auth.ErrUnauthorized,
   105  		}, {
   106  			name:      "Unauthorized:Normal->Normal",
   107  			userID:    "foo",
   108  			role:      &roleNormal,
   109  			user:      &user.NewUser{Role: auth.RoleNormal, Password: "foo"},
   110  			wantError: auth.ErrUnauthorized,
   111  		},
   112  	} {
   113  		t.Run(test.name, func(tt *testing.T) {
   114  			ctx := auth.ContextWithID(context.Background(), test.userID)
   115  			if test.role != nil {
   116  				ctx = auth.ContextWithRole(ctx, *test.role)
   117  			}
   118  
   119  			store := user.NewMemoryStore()
   120  
   121  			u := &user.User{Role: test.user.Role, Password: test.user.Password}
   122  			authorizer := user.NewAuthorizer(store)
   123  
   124  			u, err := authorizer.Create(ctx, test.user)
   125  			if err != test.wantError {
   126  				tt.Errorf("err != test.wantError: %s != %s", err, test.wantError)
   127  			}
   128  
   129  			if test.wantError == nil {
   130  				if *test.role == auth.RoleInternal {
   131  					if u.Password != "foo" {
   132  						tt.Error("Password should not be empty for internal role")
   133  					}
   134  				} else {
   135  					if u.Password != "" {
   136  						tt.Error("Password should be empty for non internal role")
   137  					}
   138  				}
   139  
   140  				u, err := store.FindByID(context.Background(), "0")
   141  				if err != nil {
   142  					t.Error(err)
   143  				}
   144  				if u == nil {
   145  					t.Error("user should have been created")
   146  				}
   147  			}
   148  		})
   149  	}
   150  }
   151  
   152  func TestAuthorizerUpdate(t *testing.T) {
   153  	for _, test := range []struct {
   154  		name      string
   155  		userID    string
   156  		role      *auth.Role
   157  		user      *user.User
   158  		wantError error
   159  	}{
   160  		{
   161  			name:      "UnauthorizedWithNoUserIDAndRole",
   162  			userID:    "",
   163  			role:      nil,
   164  			user:      &user.User{},
   165  			wantError: auth.ErrUnauthorized,
   166  		}, {
   167  			name:      "Authorized:Internal->Admin",
   168  			role:      &roleInternal,
   169  			user:      &user.User{ID: "0", Role: auth.RoleAdmin, Password: "foo"},
   170  			wantError: nil,
   171  		}, {
   172  			name:      "Unauthorized:Admin->Internal",
   173  			userID:    "0",
   174  			role:      &roleAdmin,
   175  			user:      &user.User{Role: auth.RoleInternal, Password: "foo"},
   176  			wantError: auth.ErrUnauthorized,
   177  		}, {
   178  			name:      "Authorized:Admin->Admin",
   179  			userID:    "1",
   180  			role:      &roleAdmin,
   181  			user:      &user.User{ID: "0", Role: auth.RoleAdmin, Password: "foo"},
   182  			wantError: nil,
   183  		}, {
   184  			name:      "Unauthorized:Moderator->Internal",
   185  			userID:    "1",
   186  			role:      &roleModertor,
   187  			user:      &user.User{ID: "0", Role: auth.RoleInternal, Password: "foo"},
   188  			wantError: auth.ErrUnauthorized,
   189  		}, {
   190  			name:      "Unauthorized:Moderator->Admin",
   191  			userID:    "1",
   192  			role:      &roleModertor,
   193  			user:      &user.User{ID: "0", Role: auth.RoleAdmin, Password: "foo"},
   194  			wantError: auth.ErrUnauthorized,
   195  		}, {
   196  			name:      "Unauthorized:Moderator->Moderator",
   197  			userID:    "1",
   198  			role:      &roleModertor,
   199  			user:      &user.User{ID: "0", Role: auth.RoleModerator},
   200  			wantError: auth.ErrUnauthorized,
   201  		}, {
   202  			name:      "Authorized:Moderator(self)->Moderator",
   203  			userID:    "0",
   204  			role:      &roleModertor,
   205  			user:      &user.User{ID: "0", Role: auth.RoleModerator, Password: "foo"},
   206  			wantError: nil,
   207  		}, {
   208  			name:      "Authorized:Moderator->Normal (deactivated, role)",
   209  			userID:    "0",
   210  			role:      &roleModertor,
   211  			user:      &user.User{ID: "0", Role: auth.RoleModerator, Nickname: "foo", Deactivated: true},
   212  			wantError: nil,
   213  		}, {
   214  			name:      "Unauthorized:Moderator->Normal (nickname, ..)",
   215  			userID:    "1",
   216  			role:      &roleModertor,
   217  			user:      &user.User{ID: "0", Role: auth.RoleNormal, Nickname: "changed", Deactivated: false},
   218  			wantError: auth.ErrUnauthorized,
   219  		}, {
   220  			name:      "Unauthorized:Normal->Internal",
   221  			userID:    "1",
   222  			role:      &roleNormal,
   223  			user:      &user.User{Role: auth.RoleInternal, Password: "foo"},
   224  			wantError: auth.ErrUnauthorized,
   225  		}, {
   226  			name:      "Unauthorized:Normal->Admin",
   227  			userID:    "1",
   228  			role:      &roleNormal,
   229  			user:      &user.User{Role: auth.RoleAdmin, Password: "foo"},
   230  			wantError: auth.ErrUnauthorized,
   231  		}, {
   232  			name:      "Unauthorized:Normal->Moderator",
   233  			userID:    "1",
   234  			role:      &roleNormal,
   235  			user:      &user.User{Role: auth.RoleModerator, Password: "foo"},
   236  			wantError: auth.ErrUnauthorized,
   237  		}, {
   238  			name:      "Unauthorized:Normal->Normal",
   239  			userID:    "1",
   240  			role:      &roleNormal,
   241  			user:      &user.User{Role: auth.RoleNormal, Password: "foo"},
   242  			wantError: auth.ErrUnauthorized,
   243  		}, {
   244  			name:      "Authorized:Normal(self->Normal",
   245  			userID:    "0",
   246  			role:      &roleNormal,
   247  			user:      &user.User{ID: "0", Role: auth.RoleNormal, Password: "foo"},
   248  			wantError: nil,
   249  		},
   250  	} {
   251  		t.Run(test.name, func(tt *testing.T) {
   252  			ctx := auth.ContextWithID(context.Background(), test.userID)
   253  			if test.role != nil {
   254  				ctx = auth.ContextWithRole(ctx, *test.role)
   255  			}
   256  
   257  			store := user.NewMemoryStore()
   258  
   259  			// ID: 0 test user that gets updated
   260  			_, err := store.Create(context.Background(), &user.NewUser{Role: test.user.Role})
   261  			if err != nil {
   262  				t.Fatal(err)
   263  			}
   264  			// ID: 1 doer
   265  			if test.role != nil {
   266  				_, err = store.Create(context.Background(), &user.NewUser{Role: *test.role})
   267  				if err != nil {
   268  					t.Fatal(err)
   269  				}
   270  			}
   271  
   272  			authorizer := user.NewAuthorizer(store)
   273  
   274  			u, err := authorizer.Update(ctx, test.user)
   275  			if err != test.wantError {
   276  				tt.Errorf("err != test.wantError: %s != %s", err, test.wantError)
   277  			}
   278  
   279  			if test.wantError == nil {
   280  				if *test.role == auth.RoleInternal {
   281  					if u.Password != "foo" {
   282  						tt.Error("Password should not be empty for internal role")
   283  					}
   284  				} else {
   285  					if u.Password != "" {
   286  						tt.Error("Password should be empty for non internal role")
   287  					}
   288  				}
   289  
   290  				u, err := store.FindByID(context.Background(), "0")
   291  				if err != nil {
   292  					t.Error(err)
   293  				}
   294  				if u == nil {
   295  					t.Error("user should have been created")
   296  				}
   297  			}
   298  		})
   299  	}
   300  }
   301  
   302  func TestAuthorizerDelete(t *testing.T) {
   303  	for _, test := range []struct {
   304  		name      string
   305  		userID    string
   306  		role      *auth.Role
   307  		wantError error
   308  	}{
   309  		{
   310  			name:      "UnauthorizedWithNoUserIDAndRole",
   311  			userID:    "",
   312  			role:      nil,
   313  			wantError: auth.ErrUnauthorized,
   314  		}, {
   315  			name:      "Authorized:Internal",
   316  			userID:    "1",
   317  			role:      &roleInternal,
   318  			wantError: nil,
   319  		}, {
   320  			name:      "Authorized:Admin",
   321  			userID:    "1",
   322  			role:      &roleAdmin,
   323  			wantError: nil,
   324  		}, {
   325  			name:      "Unauthorized:Moderator",
   326  			userID:    "1",
   327  			role:      &roleModertor,
   328  			wantError: auth.ErrUnauthorized,
   329  		}, {
   330  			name:      "Authorized:Moderator(self)",
   331  			userID:    "0",
   332  			role:      &roleModertor,
   333  			wantError: nil,
   334  		}, {
   335  			name:      "Unauthorized:Normal",
   336  			userID:    "1",
   337  			role:      &roleNormal,
   338  			wantError: auth.ErrUnauthorized,
   339  		}, {
   340  			name:      "Authorized:Normal(self)",
   341  			userID:    "0",
   342  			role:      &roleNormal,
   343  			wantError: nil,
   344  		},
   345  	} {
   346  		t.Run(test.name, func(t *testing.T) {
   347  			ctx := auth.ContextWithID(context.Background(), test.userID)
   348  			if test.role != nil {
   349  				ctx = auth.ContextWithRole(ctx, *test.role)
   350  			}
   351  
   352  			store := user.NewMemoryStore()
   353  			// ID: 0 test user that gets deleted
   354  			_, err := store.Create(context.Background(), &user.NewUser{})
   355  			if err != nil {
   356  				t.Fatal(err)
   357  			}
   358  			// ID: 1 doer
   359  			if test.role != nil {
   360  				_, err = store.Create(context.Background(), &user.NewUser{Role: *test.role})
   361  				if err != nil {
   362  					t.Fatal(err)
   363  				}
   364  			}
   365  
   366  			authorizer := user.NewAuthorizer(store)
   367  
   368  			err = authorizer.Delete(ctx, "0")
   369  			if err != test.wantError {
   370  				t.Errorf("err != test.wantError: %s != %s", err, test.wantError)
   371  			}
   372  
   373  			if test.wantError == nil {
   374  				u, _ := store.FindByID(context.Background(), "0")
   375  				if u != nil {
   376  					t.Fatal("user should not exist anymore")
   377  				}
   378  			}
   379  		})
   380  	}
   381  }
   382  
   383  func TestAuthorizerFindByID(t *testing.T) {
   384  	for _, test := range []struct {
   385  		name      string
   386  		userID    string
   387  		role      *auth.Role
   388  		wantError error
   389  	}{
   390  		{
   391  			name:      "UnauthorizedWithNoUserIDAndRole",
   392  			userID:    "",
   393  			role:      nil,
   394  			wantError: auth.ErrUnauthorized,
   395  		}, {
   396  			name:      "Authorized:Internal",
   397  			userID:    "0",
   398  			role:      &roleInternal,
   399  			wantError: nil,
   400  		}, {
   401  			name:      "Authorized:Admin",
   402  			userID:    "0",
   403  			role:      &roleAdmin,
   404  			wantError: nil,
   405  		}, {
   406  			name:      "Unauthorized:Moderator",
   407  			userID:    "1",
   408  			role:      &roleModertor,
   409  			wantError: nil,
   410  		}, {
   411  			name:      "Unauthorized:Normal",
   412  			userID:    "1",
   413  			role:      &roleNormal,
   414  			wantError: auth.ErrUnauthorized,
   415  		}, {
   416  			name:      "Authorized:Normal(self)",
   417  			userID:    "0",
   418  			role:      &roleNormal,
   419  			wantError: nil,
   420  		},
   421  	} {
   422  		t.Run(test.name, func(tt *testing.T) {
   423  			ctx := auth.ContextWithID(context.Background(), test.userID)
   424  			if test.role != nil {
   425  				ctx = auth.ContextWithRole(ctx, *test.role)
   426  			}
   427  
   428  			memoryStore := user.NewMemoryStore()
   429  			role := auth.RoleNormal
   430  			if test.role != nil {
   431  				role = *test.role
   432  			}
   433  			_, err := memoryStore.Create(context.Background(), &user.NewUser{
   434  				Role:     role,
   435  				Password: "foo",
   436  			})
   437  			if err != nil {
   438  				log.Fatal(err)
   439  			}
   440  			authorizer := user.NewAuthorizer(memoryStore)
   441  
   442  			u, err := authorizer.FindByID(ctx, "0")
   443  			if err != test.wantError {
   444  				tt.Errorf("err != test.wantError: %s != %s", err, test.wantError)
   445  				return
   446  			}
   447  
   448  			if test.wantError == nil {
   449  				if u == nil {
   450  					tt.Errorf("u should not be nil")
   451  				} else {
   452  					if *test.role == auth.RoleInternal {
   453  						if u.Password != "foo" {
   454  							tt.Error("Password should not be empty for internal role")
   455  						}
   456  					} else {
   457  						if u.Password != "" {
   458  							tt.Error("Password should be empty for non internal role")
   459  						}
   460  					}
   461  				}
   462  			}
   463  		})
   464  	}
   465  }
   466  
   467  func TestAuthorizerFind(t *testing.T) {
   468  	user1 := &user.User{
   469  		ID:       "0",
   470  		Email:    "secret",
   471  		Nickname: "bli",
   472  		Password: "foo",
   473  		Role:     auth.RoleAdmin,
   474  	}
   475  	user2 := &user.User{
   476  		ID:       "1",
   477  		Email:    "secret",
   478  		Nickname: "bla",
   479  		Password: "foo",
   480  		Role:     auth.RoleModerator,
   481  	}
   482  	user3 := &user.User{
   483  		ID:       "2",
   484  		Email:    "secret",
   485  		Nickname: "blub",
   486  		Password: "foo",
   487  		Role:     auth.RoleNormal,
   488  	}
   489  	users := []*user.User{user1, user2, user3}
   490  	for _, test := range []struct {
   491  		name      string
   492  		userID    string
   493  		role      *auth.Role
   494  		wantUsers []*user.User
   495  		wantError error
   496  	}{
   497  		{
   498  			name:      "UnauthorizedWithNoUserIDAndRole",
   499  			userID:    "",
   500  			role:      nil,
   501  			wantUsers: nil,
   502  			wantError: auth.ErrUnauthorized,
   503  		}, {
   504  			name:      "Authorized:Internal",
   505  			userID:    "foo",
   506  			role:      &roleInternal,
   507  			wantUsers: users,
   508  			wantError: nil,
   509  		}, {
   510  			name:   "Authorized:Admin",
   511  			userID: "foo",
   512  			role:   &roleAdmin,
   513  			wantUsers: []*user.User{
   514  				{
   515  					ID:       "0",
   516  					Email:    "secret",
   517  					Nickname: "bli",
   518  					Role:     auth.RoleAdmin,
   519  				},
   520  				{
   521  					ID:       "1",
   522  					Email:    "secret",
   523  					Nickname: "bla",
   524  					Role:     auth.RoleModerator,
   525  				},
   526  				{
   527  					ID:       "2",
   528  					Email:    "secret",
   529  					Nickname: "blub",
   530  					Role:     auth.RoleNormal,
   531  				},
   532  			},
   533  			wantError: nil,
   534  		}, {
   535  			name:   "Authorized:Moderator",
   536  			userID: "foo",
   537  			role:   &roleModertor,
   538  			wantUsers: []*user.User{
   539  				{
   540  					ID:       "0",
   541  					Email:    "secret",
   542  					Nickname: "bli",
   543  					Role:     auth.RoleAdmin,
   544  				},
   545  				{
   546  					ID:       "1",
   547  					Email:    "secret",
   548  					Nickname: "bla",
   549  					Role:     auth.RoleModerator,
   550  				},
   551  				{
   552  					ID:       "2",
   553  					Email:    "secret",
   554  					Nickname: "blub",
   555  					Role:     auth.RoleNormal,
   556  				},
   557  			},
   558  			wantError: nil,
   559  		}, {
   560  			name:   "Unauthorized:Normal",
   561  			userID: "foo",
   562  			role:   &roleNormal,
   563  			wantUsers: []*user.User{
   564  				{ID: "0", Nickname: "bli"},
   565  				{ID: "1", Nickname: "bla"},
   566  				{ID: "2", Nickname: "blub"},
   567  			},
   568  			wantError: nil,
   569  		},
   570  	} {
   571  		t.Run(test.name, func(tt *testing.T) {
   572  			ctx := auth.ContextWithID(context.Background(), test.userID)
   573  			if test.role != nil {
   574  				ctx = auth.ContextWithRole(ctx, *test.role)
   575  			}
   576  
   577  			memoryStore := user.NewMemoryStore()
   578  			for _, u := range users {
   579  				memoryStore.Create(context.Background(), &user.NewUser{
   580  					Nickname: u.Nickname,
   581  					Email:    u.Email,
   582  					Password: u.Password,
   583  					Role:     u.Role,
   584  				})
   585  			}
   586  			authorizer := user.NewAuthorizer(memoryStore)
   587  
   588  			users, _, err := authorizer.Find(ctx, nil)
   589  			if err != test.wantError {
   590  				tt.Errorf("err != test.wantError: %s != %s", err, test.wantError)
   591  			}
   592  
   593  			if test.wantError == nil {
   594  				if diff := cmp.Diff(test.wantUsers, users, cmpopts.IgnoreFields(user.User{}, "CreatedAt")); diff != "" {
   595  					tt.Errorf("users return mismatch: %s", diff)
   596  				}
   597  				if *test.role == auth.RoleInternal {
   598  					for _, user := range users {
   599  						if user.Password != "foo" {
   600  							tt.Error("Password should not be empty for internal role")
   601  						}
   602  					}
   603  				} else {
   604  					for _, user := range users {
   605  						if user.Password != "" {
   606  							tt.Error("Password should be empty for non internal role")
   607  						}
   608  					}
   609  				}
   610  			}
   611  		})
   612  	}
   613  }