From bbe83c2af6c199a8a0613d87eec574cc7b5d220d Mon Sep 17 00:00:00 2001 From: Dominic Wong Date: Wed, 2 Mar 2022 17:09:45 +0000 Subject: [PATCH] Add switches to password/generate (#396) --- password/examples.json | 35 ++++++++++++---- password/handler/password.go | 67 ++++++++++++++++++++++-------- password/proto/password.pb.go | 77 +++++++++++++++++++++++++++-------- password/proto/password.proto | 13 +++++- 4 files changed, 146 insertions(+), 46 deletions(-) diff --git a/password/examples.json b/password/examples.json index 9b5a6cf..b3077bb 100644 --- a/password/examples.json +++ b/password/examples.json @@ -1,14 +1,31 @@ { - "generate": [{ - "title": "Generate password", - "description": "Generate a strong password", - "run_check": false, - "request": { - "length": 16 + "generate": [ + { + "title": "Generate password", + "description": "Generate a strong password", + "run_check": false, + "request": { + "length": 16 + }, + "response": { + "password": "jSUHz74x8qW#aXRe" + } }, - "response": { - "password": "jSUHz74x8qW#aXRe" + { + "title": "Generate password without special characters", + "description": "Generate a password without special characters", + "run_check": true, + "request": { + "length": 16, + "lowercase": true, + "numbers": true, + "special": false, + "uppercase": true + }, + "response": { + "password": "Gia6II0TQePjkql1" + } } - }] + ] } diff --git a/password/handler/password.go b/password/handler/password.go index c236f2d..c320a7d 100644 --- a/password/handler/password.go +++ b/password/handler/password.go @@ -3,36 +3,32 @@ package handler import ( "context" "crypto/rand" + "strings" pb "github.com/micro/services/password/proto" ) - -var ( +const ( minLength = 8 - special = "!@#$%&*" - numbers = "0123456789" + special = "!@#$%&*" + numbers = "0123456789" lowercase = "abcdefghijklmnopqrstuvwxyz" uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - allChars = special + numbers + lowercase + uppercase + allChars = special + numbers + lowercase + uppercase ) func random(chars string, i int) string { - bytes := make([]byte, i) + bytes := make([]byte, i) - for { - rand.Read(bytes) - for i, b := range bytes { - bytes[i] = chars[b%byte(len(chars))] - } - break - } + rand.Read(bytes) + for i, b := range bytes { + bytes[i] = chars[b%byte(len(chars))] + } - return string(bytes) + return string(bytes) } - type Password struct{} func (p *Password) Generate(ctx context.Context, req *pb.GenerateRequest, rsp *pb.GenerateResponse) error { @@ -40,10 +36,45 @@ func (p *Password) Generate(ctx context.Context, req *pb.GenerateRequest, rsp *p req.Length = int32(minLength) } - // TODO; allow user to specify types of characters + charSpace := "" + if req.Numbers { + charSpace += numbers + } + if req.Lowercase { + charSpace += lowercase + } + if req.Uppercase { + charSpace += uppercase + } + if req.Special { + charSpace += special + } + if len(charSpace) == 0 { + charSpace = allChars + } - // generate and return password - rsp.Password = random(allChars, int(req.Length)) + for { + // generate and return password + rsp.Password = random(charSpace, int(req.Length)) + // validate we have minimums needed + reqsSatisfied := true + if req.Numbers { + reqsSatisfied = reqsSatisfied && strings.ContainsAny(rsp.Password, numbers) + } + if req.Lowercase { + reqsSatisfied = reqsSatisfied && strings.ContainsAny(rsp.Password, lowercase) + } + if req.Uppercase { + reqsSatisfied = reqsSatisfied && strings.ContainsAny(rsp.Password, uppercase) + } + if req.Special { + reqsSatisfied = reqsSatisfied && strings.ContainsAny(rsp.Password, special) + } + if reqsSatisfied { + break + } + // failed to satisfy all reqs, try again + } return nil } diff --git a/password/proto/password.pb.go b/password/proto/password.pb.go index 5c7531a..a23d787 100644 --- a/password/proto/password.pb.go +++ b/password/proto/password.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 -// protoc v3.15.6 +// protoc-gen-go v1.26.0 +// protoc v3.15.5 // source: proto/password.proto package password @@ -20,14 +20,22 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// Generate a strong random password +// Generate a strong random password. Use the switches to control which character types are included, defaults to using all of them type GenerateRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // password length; defaults to 16 chars + // password length; defaults to 8 chars Length int32 `protobuf:"varint,1,opt,name=length,proto3" json:"length,omitempty"` + // include lowercase letters + Lowercase bool `protobuf:"varint,2,opt,name=lowercase,proto3" json:"lowercase,omitempty"` + // include uppercase letters + Uppercase bool `protobuf:"varint,3,opt,name=uppercase,proto3" json:"uppercase,omitempty"` + // include numbers + Numbers bool `protobuf:"varint,4,opt,name=numbers,proto3" json:"numbers,omitempty"` + // include special characters (!@#$%&*) + Special bool `protobuf:"varint,5,opt,name=special,proto3" json:"special,omitempty"` } func (x *GenerateRequest) Reset() { @@ -69,6 +77,34 @@ func (x *GenerateRequest) GetLength() int32 { return 0 } +func (x *GenerateRequest) GetLowercase() bool { + if x != nil { + return x.Lowercase + } + return false +} + +func (x *GenerateRequest) GetUppercase() bool { + if x != nil { + return x.Uppercase + } + return false +} + +func (x *GenerateRequest) GetNumbers() bool { + if x != nil { + return x.Numbers + } + return false +} + +func (x *GenerateRequest) GetSpecial() bool { + if x != nil { + return x.Special + } + return false +} + type GenerateResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -122,19 +158,26 @@ var File_proto_password_proto protoreflect.FileDescriptor var file_proto_password_proto_rawDesc = []byte{ 0x0a, 0x14, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x22, 0x29, 0x0a, 0x0f, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x2e, 0x0a, 0x10, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x32, 0x4f, 0x0a, 0x08, 0x50, - 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x43, 0x0a, 0x08, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x61, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, - 0x2e, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x12, 0x5a, 0x10, - 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x22, 0x99, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x1c, 0x0a, 0x09, + 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x09, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x70, + 0x70, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x75, + 0x70, 0x70, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x6e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x22, 0x2e, 0x0a, 0x10, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x32, 0x4f, 0x0a, 0x08, + 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x43, 0x0a, 0x08, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x2e, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1a, 0x2e, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x12, 0x5a, + 0x10, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, + 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/password/proto/password.proto b/password/proto/password.proto index 728e935..487d052 100644 --- a/password/proto/password.proto +++ b/password/proto/password.proto @@ -8,10 +8,19 @@ service Password { rpc Generate(GenerateRequest) returns (GenerateResponse) {} } -// Generate a strong random password +// Generate a strong random password. Use the switches to control which character types are included, defaults to using all of them message GenerateRequest { - // password length; defaults to 16 chars + // password length; defaults to 8 chars int32 length = 1; + // include lowercase letters + bool lowercase = 2; + // include uppercase letters + bool uppercase = 3; + // include numbers + bool numbers = 4; + // include special characters (!@#$%&*) + bool special = 5; + } message GenerateResponse {