author | Alberto Bertogli
<albertito@blitiri.com.ar> 2023-12-03 00:12:46 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2023-12-03 11:59:26 UTC |
parent | dbff2f04553ca9bda575ca395d623176a678ffa6 |
cmd/chasquid-util/chasquid-util.go | +17 | -4 |
cmd/chasquid-util/test.sh | +16 | -0 |
docs/man/chasquid-util.1 | +7 | -4 |
docs/man/chasquid-util.1.md | +5 | -2 |
docs/man/chasquid-util.1.pod | +5 | -2 |
internal/userdb/userdb.go | +18 | -0 |
internal/userdb/userdb.pb.go | +103 | -32 |
internal/userdb/userdb.proto | +3 | -0 |
internal/userdb/userdb_test.go | +39 | -3 |
diff --git a/cmd/chasquid-util/chasquid-util.go b/cmd/chasquid-util/chasquid-util.go index 2a48986..792da96 100644 --- a/cmd/chasquid-util/chasquid-util.go +++ b/cmd/chasquid-util/chasquid-util.go @@ -29,7 +29,7 @@ import ( // Usage to show users on --help or invocation errors. const usage = ` Usage: - chasquid-util [options] user-add <user@domain> [--password=<password>] + chasquid-util [options] user-add <user@domain> [--password=<password>] [--receive_only] chasquid-util [options] user-remove <user@domain> chasquid-util [options] authenticate <user@domain> [--password=<password>] chasquid-util [options] check-userdb <domain> @@ -140,12 +140,25 @@ func checkUserDB() { fmt.Println("Database loaded") } -// chasquid-util user-add <user@domain> [--password=<password>] +// chasquid-util user-add <user@domain> [--password=<password>] [--receive_only] func userAdd() { user, _, db := userDBFromArgs(true) - password := getPassword() - err := db.AddUser(user, password) + _, recvOnly := args["--receive_only"] + _, hasPassword := args["--password"] + + if recvOnly && hasPassword { + Fatalf("Cannot specify both --receive_only and --password") + } + + var err error + if recvOnly { + err = db.AddDeniedUser(user) + } else { + password := getPassword() + err = db.AddUser(user, password) + } + if err != nil { Fatalf("Error adding user: %v", err) } diff --git a/cmd/chasquid-util/test.sh b/cmd/chasquid-util/test.sh index c659435..0fdc9dc 100755 --- a/cmd/chasquid-util/test.sh +++ b/cmd/chasquid-util/test.sh @@ -34,6 +34,17 @@ if ! r user-add user@domain --password=passwd > /dev/null; then fi check_userdb +if ! r user-add denied@domain --receive_only > /dev/null; then + echo user-add --receive_only failed + exit 1 +fi +check_userdb + +if r user-add xxx@domain --password=passwd --receive_only > /dev/null; then + echo user-add --password --receive_only worked + exit 1 +fi + if ! r authenticate user@domain --password=passwd > /dev/null; then echo authenticate failed exit 1 @@ -44,6 +55,11 @@ if r authenticate user@domain --password=abcd > /dev/null; then exit 1 fi +if r authenticate denied@domain --password=abcd > /dev/null; then + echo authenticate on a no-submission user worked + exit 1 +fi + # Interactive authentication. # Need to wrap the execution under "script" since the interaction requires an # actual TTY, and that's a fairly portable way to do that. diff --git a/docs/man/chasquid-util.1 b/docs/man/chasquid-util.1 index e8e1252..31e0a53 100644 --- a/docs/man/chasquid-util.1 +++ b/docs/man/chasquid-util.1 @@ -133,7 +133,7 @@ .\" ======================================================================== .\" .IX Title "chasquid-util 1" -.TH chasquid-util 1 "2023-07-29" "" "" +.TH chasquid-util 1 "2023-12-03" "" "" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -142,7 +142,7 @@ chasquid\-util \- chasquid management tool .SH "SYNOPSIS" .IX Header "SYNOPSIS" -\&\fBchasquid-util\fR [\fIoptions\fR] user-add \fIuser@domain\fR [\-\-password=\fIpassword\fR] +\&\fBchasquid-util\fR [\fIoptions\fR] user-add \fIuser@domain\fR [\-\-password=\fIpassword\fR] [\-\-receive_only] .PP \&\fBchasquid-util\fR [\fIoptions\fR] user-remove \fIuser@domain\fR .PP @@ -160,9 +160,12 @@ chasquid\-util \- chasquid management tool chasquid-util is a command-line utility for \fBchasquid\fR\|(1) operations. .SH "OPTIONS" .IX Header "OPTIONS" -.IP "\fBuser-add\fR \fIuser@domain\fR [\-\-password=\fIpassword\fR]" 8 -.IX Item "user-add user@domain [--password=password]" +.IP "\fBuser-add\fR \fIuser@domain\fR [\-\-password=\fIpassword\fR] [\-\-receive_only]" 8 +.IX Item "user-add user@domain [--password=password] [--receive_only]" Add a new user to the domain. +.Sp +If \fI\-\-receive_only\fR is given, then the user will never successfully +authenticate. This is useful when creating receive-only users. .IP "\fBuser-remove\fR \fIuser@domain\fR" 8 .IX Item "user-remove user@domain" Remove the user from the domain. diff --git a/docs/man/chasquid-util.1.md b/docs/man/chasquid-util.1.md index e3ebfca..13841f6 100644 --- a/docs/man/chasquid-util.1.md +++ b/docs/man/chasquid-util.1.md @@ -4,7 +4,7 @@ chasquid-util - chasquid management tool # SYNOPSIS -**chasquid-util** \[_options_\] user-add _user@domain_ \[--password=_password_\] +**chasquid-util** \[_options_\] user-add _user@domain_ \[--password=_password_\] \[--receive\_only\] **chasquid-util** \[_options_\] user-remove _user@domain_ @@ -24,10 +24,13 @@ chasquid-util is a command-line utility for [chasquid(1)](chasquid.1.md) operati # OPTIONS -- **user-add** _user@domain_ \[--password=_password_\] +- **user-add** _user@domain_ \[--password=_password_\] \[--receive\_only\] Add a new user to the domain. + If _--receive\_only_ is given, then the user will never successfully + authenticate. This is useful when creating receive-only users. + - **user-remove** _user@domain_ Remove the user from the domain. diff --git a/docs/man/chasquid-util.1.pod b/docs/man/chasquid-util.1.pod index 8976d1e..f060f97 100644 --- a/docs/man/chasquid-util.1.pod +++ b/docs/man/chasquid-util.1.pod @@ -5,7 +5,7 @@ chasquid-util - chasquid management tool =head1 SYNOPSIS -B<chasquid-util> [I<options>] user-add I<user@domain> [--password=I<password>] +B<chasquid-util> [I<options>] user-add I<user@domain> [--password=I<password>] [--receive_only] B<chasquid-util> [I<options>] user-remove I<user@domain> @@ -28,10 +28,13 @@ chasquid-util is a command-line utility for chasquid(1) operations. =over 8 -=item B<user-add> I<user@domain> [--password=I<password>] +=item B<user-add> I<user@domain> [--password=I<password>] [--receive_only] Add a new user to the domain. +If I<--receive_only> is given, then the user will never successfully +authenticate. This is useful when creating receive-only users. + =item B<user-remove> I<user@domain> Remove the user from the domain. diff --git a/internal/userdb/userdb.go b/internal/userdb/userdb.go index 8b07c46..76e43d6 100644 --- a/internal/userdb/userdb.go +++ b/internal/userdb/userdb.go @@ -123,6 +123,8 @@ func (p *Password) PasswordMatches(plain string) bool { return s.Scrypt.PasswordMatches(plain) case *Password_Plain: return s.Plain.PasswordMatches(plain) + case *Password_Denied: + return false default: return false } @@ -164,6 +166,22 @@ func (db *DB) AddUser(name, plainPassword string) error { return nil } +// AddDenied to the database. If the user is already present, override it. +// Note we enforce that the name has been normalized previously. +func (db *DB) AddDeniedUser(name string) error { + if norm, err := normalize.User(name); err != nil || name != norm { + return errors.New("invalid username") + } + + db.mu.Lock() + db.db.Users[name] = &Password{ + Scheme: &Password_Denied{&Denied{}}, + } + db.mu.Unlock() + + return nil +} + // RemoveUser from the database. Returns True if the user was there, False // otherwise. func (db *DB) RemoveUser(name string) bool { diff --git a/internal/userdb/userdb.pb.go b/internal/userdb/userdb.pb.go index 9b734ff..63ec0d2 100644 --- a/internal/userdb/userdb.pb.go +++ b/internal/userdb/userdb.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 +// protoc-gen-go v1.30.0 // protoc v3.21.12 // source: userdb.proto @@ -73,8 +73,10 @@ type Password struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Scheme: + // // *Password_Scrypt // *Password_Plain + // *Password_Denied Scheme isPassword_Scheme `protobuf_oneof:"scheme"` } @@ -131,6 +133,13 @@ func (x *Password) GetPlain() *Plain { return nil } +func (x *Password) GetDenied() *Denied { + if x, ok := x.GetScheme().(*Password_Denied); ok { + return x.Denied + } + return nil +} + type isPassword_Scheme interface { isPassword_Scheme() } @@ -143,10 +152,16 @@ type Password_Plain struct { Plain *Plain `protobuf:"bytes,3,opt,name=plain,proto3,oneof"` } +type Password_Denied struct { + Denied *Denied `protobuf:"bytes,4,opt,name=denied,proto3,oneof"` +} + func (*Password_Scrypt) isPassword_Scheme() {} func (*Password_Plain) isPassword_Scheme() {} +func (*Password_Denied) isPassword_Scheme() {} + type Scrypt struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -281,6 +296,44 @@ func (x *Plain) GetPassword() []byte { return nil } +type Denied struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Denied) Reset() { + *x = Denied{} + if protoimpl.UnsafeEnabled { + mi := &file_userdb_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Denied) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Denied) ProtoMessage() {} + +func (x *Denied) ProtoReflect() protoreflect.Message { + mi := &file_userdb_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Denied.ProtoReflect.Descriptor instead. +func (*Denied) Descriptor() ([]byte, []int) { + return file_userdb_proto_rawDescGZIP(), []int{4} +} + var File_userdb_proto protoreflect.FileDescriptor var file_userdb_proto_rawDesc = []byte{ @@ -294,27 +347,30 @@ var file_userdb_proto_rawDesc = []byte{ 0x03, 0x6b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x64, 0x62, 0x2e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x22, 0x65, 0x0a, 0x08, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x28, 0x0a, 0x06, - 0x73, 0x63, 0x72, 0x79, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x75, - 0x73, 0x65, 0x72, 0x64, 0x62, 0x2e, 0x53, 0x63, 0x72, 0x79, 0x70, 0x74, 0x48, 0x00, 0x52, 0x06, - 0x73, 0x63, 0x72, 0x79, 0x70, 0x74, 0x12, 0x25, 0x0a, 0x05, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x64, 0x62, 0x2e, 0x50, - 0x6c, 0x61, 0x69, 0x6e, 0x48, 0x00, 0x52, 0x05, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x42, 0x08, 0x0a, - 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x22, 0x82, 0x01, 0x0a, 0x06, 0x53, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x4e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x4e, 0x12, 0x0c, 0x0a, 0x01, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x01, 0x72, 0x12, 0x0c, 0x0a, 0x01, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x01, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x4c, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x06, 0x6b, 0x65, 0x79, 0x4c, 0x65, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x61, - 0x6c, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x73, 0x61, 0x6c, 0x74, 0x12, 0x1c, - 0x0a, 0x09, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x09, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x22, 0x23, 0x0a, 0x05, - 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, - 0x64, 0x42, 0x2c, 0x5a, 0x2a, 0x62, 0x6c, 0x69, 0x74, 0x69, 0x72, 0x69, 0x2e, 0x63, 0x6f, 0x6d, - 0x2e, 0x61, 0x72, 0x2f, 0x67, 0x6f, 0x2f, 0x63, 0x68, 0x61, 0x73, 0x71, 0x75, 0x69, 0x64, 0x2f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x64, 0x62, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x22, 0x8f, 0x01, 0x0a, 0x08, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x28, 0x0a, + 0x06, 0x73, 0x63, 0x72, 0x79, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, + 0x75, 0x73, 0x65, 0x72, 0x64, 0x62, 0x2e, 0x53, 0x63, 0x72, 0x79, 0x70, 0x74, 0x48, 0x00, 0x52, + 0x06, 0x73, 0x63, 0x72, 0x79, 0x70, 0x74, 0x12, 0x25, 0x0a, 0x05, 0x70, 0x6c, 0x61, 0x69, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x64, 0x62, 0x2e, + 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x48, 0x00, 0x52, 0x05, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x12, 0x28, + 0x0a, 0x06, 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, + 0x2e, 0x75, 0x73, 0x65, 0x72, 0x64, 0x62, 0x2e, 0x44, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x48, 0x00, + 0x52, 0x06, 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x42, 0x08, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x65, 0x22, 0x82, 0x01, 0x0a, 0x06, 0x53, 0x63, 0x72, 0x79, 0x70, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x6c, 0x6f, 0x67, 0x4e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x6c, 0x6f, 0x67, + 0x4e, 0x12, 0x0c, 0x0a, 0x01, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, 0x72, 0x12, + 0x0c, 0x0a, 0x01, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, 0x70, 0x12, 0x16, 0x0a, + 0x06, 0x6b, 0x65, 0x79, 0x4c, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6b, + 0x65, 0x79, 0x4c, 0x65, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x61, 0x6c, 0x74, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x04, 0x73, 0x61, 0x6c, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x6e, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x65, 0x6e, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x22, 0x23, 0x0a, 0x05, 0x50, 0x6c, 0x61, 0x69, 0x6e, + 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x08, 0x0a, 0x06, + 0x44, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x42, 0x2c, 0x5a, 0x2a, 0x62, 0x6c, 0x69, 0x74, 0x69, 0x72, + 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x72, 0x2f, 0x67, 0x6f, 0x2f, 0x63, 0x68, 0x61, 0x73, + 0x71, 0x75, 0x69, 0x64, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x75, 0x73, + 0x65, 0x72, 0x64, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -329,24 +385,26 @@ func file_userdb_proto_rawDescGZIP() []byte { return file_userdb_proto_rawDescData } -var file_userdb_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_userdb_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_userdb_proto_goTypes = []interface{}{ (*ProtoDB)(nil), // 0: userdb.ProtoDB (*Password)(nil), // 1: userdb.Password (*Scrypt)(nil), // 2: userdb.Scrypt (*Plain)(nil), // 3: userdb.Plain - nil, // 4: userdb.ProtoDB.UsersEntry + (*Denied)(nil), // 4: userdb.Denied + nil, // 5: userdb.ProtoDB.UsersEntry } var file_userdb_proto_depIdxs = []int32{ - 4, // 0: userdb.ProtoDB.users:type_name -> userdb.ProtoDB.UsersEntry + 5, // 0: userdb.ProtoDB.users:type_name -> userdb.ProtoDB.UsersEntry 2, // 1: userdb.Password.scrypt:type_name -> userdb.Scrypt 3, // 2: userdb.Password.plain:type_name -> userdb.Plain - 1, // 3: userdb.ProtoDB.UsersEntry.value:type_name -> userdb.Password - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 4, // 3: userdb.Password.denied:type_name -> userdb.Denied + 1, // 4: userdb.ProtoDB.UsersEntry.value:type_name -> userdb.Password + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name } func init() { file_userdb_proto_init() } @@ -403,10 +461,23 @@ func file_userdb_proto_init() { return nil } } + file_userdb_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Denied); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_userdb_proto_msgTypes[1].OneofWrappers = []interface{}{ (*Password_Scrypt)(nil), (*Password_Plain)(nil), + (*Password_Denied)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -414,7 +485,7 @@ func file_userdb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_userdb_proto_rawDesc, NumEnums: 0, - NumMessages: 5, + NumMessages: 6, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/userdb/userdb.proto b/internal/userdb/userdb.proto index b2a536c..e23f24a 100644 --- a/internal/userdb/userdb.proto +++ b/internal/userdb/userdb.proto @@ -12,6 +12,7 @@ message Password { oneof scheme { Scrypt scrypt = 2; Plain plain = 3; + Denied denied = 4; } } @@ -27,3 +28,5 @@ message Scrypt { message Plain { bytes password = 1; } + +message Denied { } diff --git a/internal/userdb/userdb_test.go b/internal/userdb/userdb_test.go index 2c9f018..cc1445f 100644 --- a/internal/userdb/userdb_test.go +++ b/internal/userdb/userdb_test.go @@ -122,24 +122,27 @@ func TestWrite(t *testing.T) { t.Fatalf("expected %v, got %v", emptyDB, db) } - // Add two users, write, and load again. + // Add users, write, and load again. if err := db.AddUser("user1", "passwd1"); err != nil { t.Fatalf("failed to add user1: %v", err) } if err := db.AddUser("ñoño", "añicos"); err != nil { t.Fatalf("failed to add ñoño: %v", err) } + if err := db.AddDeniedUser("ñaca"); err != nil { + t.Fatalf("failed to add ñaca: %v", err) + } if err := db.Write(); err != nil { t.Fatalf("error writing database: %v", err) } db = mustLoad(t, fname) - for _, name := range []string{"user1", "ñoño"} { + for _, name := range []string{"user1", "ñoño", "ñaca"} { if !db.Exists(name) { t.Errorf("user %q not in database", name) } if db.db.Users[name].GetScheme() == nil { - t.Errorf("user %q not using scrypt: %#v", name, db.db.Users[name]) + t.Errorf("user %q missing scheme: %#v", name, db.db.Users[name]) } } @@ -153,6 +156,8 @@ func TestWrite(t *testing.T) { {"user1", "passwd12", false}, {"ñoño", "añicos", true}, {"ñoño", "anicos", false}, + {"ñaca", "", false}, + {"ñaca", "lalala", false}, {"notindb", "something", false}, {"", "", false}, {" ", " ", false}, @@ -202,6 +207,11 @@ func TestInvalidUsername(t *testing.T) { if err == nil { t.Errorf("AddUser(%q) worked, expected it to fail", name) } + + err = db.AddDeniedUser(name) + if err == nil { + t.Errorf("AddDeniedUser(%q) worked, expected it to fail", name) + } } } @@ -235,6 +245,24 @@ func TestPlainScheme(t *testing.T) { } } +// Test the denied scheme. +func TestDeniedScheme(t *testing.T) { + fname := mustCreateDB(t, "") + defer removeIfSuccessful(t, fname) + db := mustLoad(t, fname) + + db.db.Users["user"] = &Password{Scheme: &Password_Denied{}} + err := db.Write() + if err != nil { + t.Errorf("Write failed: %v", err) + } + + db = mustLoad(t, fname) + if db.Authenticate("user", "anything") { + t.Errorf("denied authentication worked but it shouldn't") + } +} + func TestReload(t *testing.T) { content := "users:< key: 'u1' value:< plain:< password: 'pass' >>>" fname := mustCreateDB(t, content) @@ -326,4 +354,12 @@ func TestExists(t *testing.T) { if !db.Exists("user") { t.Errorf("known user does not exist") } + + if err := db.AddDeniedUser("denieduser"); err != nil { + t.Fatalf("error adding user: %v", err) + } + + if !db.Exists("denieduser") { + t.Errorf("known (denied) user does not exist") + } }