Skip to content

Commit

Permalink
encrypt password of corp managers (#81)
Browse files Browse the repository at this point in the history
* encrypt password of corp managers

* minor update

* do more update

* update api
  • Loading branch information
zengchen1024 authored Mar 17, 2021
1 parent d5565f1 commit e419ef6
Show file tree
Hide file tree
Showing 12 changed files with 181 additions and 27 deletions.
6 changes: 6 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ type appConfig struct {
CLAFieldsNumber int `json:"cla_fields_number" required:"true"`
MaxSizeOfCorpCLAPDF int `json:"max_size_of_corp_cla_pdf"`
MaxSizeOfOrgSignaturePDF int `json:"max_size_of_org_signature_pdf"`
MinLengthOfPassword int `json:"min_length_of_password"`
MaxLengthOfPassword int `json:"max_length_of_password"`
VerificationCodeExpiry int64 `json:"verification_code_expiry" required:"true"`
APITokenExpiry int64 `json:"api_token_expiry" required:"true"`
APITokenKey string `json:"api_token_key" required:"true"`
Expand Down Expand Up @@ -47,6 +49,8 @@ func InitAppConfig() error {

maxSizeOfCorpCLAPDF := beego.AppConfig.DefaultInt("max_size_of_corp_cla_pdf", (2 << 20))
maxSizeOfOrgSignaturePDF := beego.AppConfig.DefaultInt("max_size_of_org_signature_pdf", (1 << 20))
minLengthOfPassword := beego.AppConfig.DefaultInt("min_length_of_password", 6)
maxLengthOfPassword := beego.AppConfig.DefaultInt("max_length_of_password", 16)

tokenExpiry, err := beego.AppConfig.Int64("api_token_expiry")
if err != nil {
Expand All @@ -68,6 +72,8 @@ func InitAppConfig() error {
CLAFieldsNumber: claFieldsNumber,
MaxSizeOfCorpCLAPDF: maxSizeOfCorpCLAPDF,
MaxSizeOfOrgSignaturePDF: maxSizeOfOrgSignaturePDF,
MinLengthOfPassword: minLengthOfPassword,
MaxLengthOfPassword: maxLengthOfPassword,
VerificationCodeExpiry: codeExpiry,
APITokenExpiry: tokenExpiry,
APITokenKey: beego.AppConfig.String("api_token_key"),
Expand Down
24 changes: 20 additions & 4 deletions controllers/corporation-manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,21 @@ func (this *CorporationManagerController) Put() {
}

// @Title Patch
// @Description reset password of corporation administrator
// @Success 204 {int} map
// @Failure util.ErrInvalidAccountOrPw
// @Description reset password of corporation manager
// @Param body body dbmodels.CorporationManagerResetPassword true "body for resetting password"
// @Success 204 {string} "reset password successfully"
// @Failure 401 missing_token: token is missing
// @Failure 402 unknown_token: token is unknown
// @Failure 403 expired_token: token is expired
// @Failure 404 unauthorized_token: the permission of token is unmatched
// @Failure 405 error_parsing_api_body: parse payload of request failed
// @Failure 406 same_password: the old and new passwords are same
// @Failure 407 too_short_or_long_password: the length of new password is too short or long
// @Failure 408 invalid_password: the format of new password is invalid
// @Failure 409 corp_manager_does_not_exist: manager may be removed
// @Failure 410 wrong_old_password: the old password is not correct
// @Failure 411 frequent_operation: don't operate frequently
// @Failure 500 system_error: system error
// @router / [patch]
func (this *CorporationManagerController) Patch() {
action := "reset password of corp's manager"
Expand All @@ -120,7 +132,11 @@ func (this *CorporationManagerController) Patch() {
}

if err := (&info).Reset(pl.LinkID, pl.Email); err != nil {
this.sendModelErrorAsResp(err, action)
if err.IsErrorOf(models.ErrNoLinkOrNoManagerOrFO) {
this.sendFailedResponse(400, errFrequentOperation, err, action)
} else {
this.sendModelErrorAsResp(err, action)
}
return
}

Expand Down
2 changes: 1 addition & 1 deletion controllers/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const (
errResigned = "resigned"
errUnsigned = string(models.ErrUnsigned)
errNoLink = string(models.ErrNoLink)
errNoEmployeeManager = "no_employee_manager"
errWrongIDOrPassword = "wrong_id_or_pw"
errCorpManagerExists = string(models.ErrCorpManagerExists)
errNoRefreshToken = "no_refresh_token"
Expand All @@ -39,6 +38,7 @@ const (
errGoToSignEmployeeCLA = "go_to_sign_employee_cla"
errUnsupportedCLALang = "unsupported_cla_lang"
errNotSameCorp = string(models.ErrNotSameCorp)
errFrequentOperation = "frequent_operation"
)

func parseModelError(err models.IModelError) *failedApiResult {
Expand Down
2 changes: 1 addition & 1 deletion dbmodels/corporation-manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ type CorporationManagerCheckInfo struct {
ID string
Email string
EmailSuffix string
Password string
}

type CorporationManagerResetPassword struct {
Expand All @@ -29,6 +28,7 @@ type CorporationManagerCheckResult struct {
Role string
Name string
Email string
Password string
InitialPWChanged bool

OrgInfo
Expand Down
1 change: 1 addition & 0 deletions dbmodels/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type ICorporationManager interface {
DeleteEmployeeManager(orgCLAID string, emails []string) ([]CorporationManagerCreateOption, IDBError)
ResetCorporationManagerPassword(string, string, CorporationManagerResetPassword) IDBError
ListCorporationManager(orgCLAID, email, role string) ([]CorporationManagerListResult, IDBError)
GetCorporationManager(linkID, email string) (*CorporationManagerCheckResult, IDBError)
}

type IOrgEmail interface {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require (
github.com/huaweicloud/golangsdk v0.0.0-20201228013212-d10065a3dc7f
github.com/opensourceways/gofpdf v1.16.4
go.mongodb.org/mongo-driver v1.4.4
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5
google.golang.org/api v0.36.0
sigs.k8s.io/yaml v1.2.0
Expand Down
1 change: 0 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github/v33 v33.0.0 h1:qAf9yP0qc54ufQxzwv+u9H0tiVOnPJxo0lI/JXqw3ZM=
github.com/google/go-github/v33 v33.0.0/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
Expand Down
71 changes: 65 additions & 6 deletions models/corporation-manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"strings"

"github.com/opensourceways/app-cla-server/config"
"github.com/opensourceways/app-cla-server/dbmodels"
"github.com/opensourceways/app-cla-server/util"
)
Expand All @@ -14,7 +15,7 @@ type CorporationManagerAuthentication struct {
}

func (this CorporationManagerAuthentication) Authenticate() (map[string]dbmodels.CorporationManagerCheckResult, IModelError) {
info := dbmodels.CorporationManagerCheckInfo{Password: this.Password}
info := dbmodels.CorporationManagerCheckInfo{}
if merr := checkEmailFormat(this.User); merr == nil {
info.Email = this.User
} else {
Expand All @@ -29,25 +30,35 @@ func (this CorporationManagerAuthentication) Authenticate() (map[string]dbmodels

v, err := dbmodels.GetDB().CheckCorporationManagerExist(info)
if err == nil {
for k := range v {
if !isSamePasswords(v[k].Password, this.Password) {
delete(v, k)
}
}
return v, nil
}

return nil, parseDBError(err)
}

func CreateCorporationAdministrator(linkID, name, email string) (*dbmodels.CorporationManagerCreateOption, IModelError) {
pw := util.RandStr(8, "alphanum")
pw := newPWForCorpManager()
encryptedPW, merr := encryptPassword(pw)
if merr != nil {
return nil, merr
}

opt := &dbmodels.CorporationManagerCreateOption{
ID: "admin",
Name: name,
Email: email,
Password: pw,
Password: encryptedPW,
Role: dbmodels.RoleAdmin,
}
err := dbmodels.GetDB().AddCorpAdministrator(linkID, opt)
if err == nil {
opt.ID = fmt.Sprintf("admin_%s", util.EmailSuffix(email))
opt.Password = pw
return opt, nil
}

Expand All @@ -64,23 +75,67 @@ func (this CorporationManagerResetPassword) Validate() IModelError {
if this.NewPassword == this.OldPassword {
return newModelError(ErrSamePassword, fmt.Errorf("the new password is same as old one"))
}
return nil

n := len(this.NewPassword)
cfg := config.AppConfig
if n < cfg.MinLengthOfPassword || n > cfg.MaxLengthOfPassword {
return newModelError(
ErrTooShortOrLongPassword,
fmt.Errorf(
"the length of password should be between %d and %d",
cfg.MinLengthOfPassword, cfg.MaxLengthOfPassword,
))
}

return checkPassword(this.NewPassword)
}

func (this CorporationManagerResetPassword) Reset(linkID, email string) IModelError {
pw, merr := encryptPassword(this.NewPassword)
if merr != nil {
return merr
}

record, merr := this.getCorporationManager(linkID, email)
if merr != nil {
return merr
}
if record == nil {
return newModelError(ErrCorpManagerDoesNotExist, fmt.Errorf("corp manager does not exist"))
}

if !isSamePasswords(record.Password, this.OldPassword) {
return newModelError(ErrWrongOldPassword, fmt.Errorf("old password is not correct"))
}

err := dbmodels.GetDB().ResetCorporationManagerPassword(
linkID, email, dbmodels.CorporationManagerResetPassword(this),
linkID, email, dbmodels.CorporationManagerResetPassword{
OldPassword: record.Password, NewPassword: pw,
},
)
if err == nil {
return nil
}

if err.IsErrorOf(dbmodels.ErrNoDBRecord) {
return newModelError(ErrNoLinkOrNoManager, err)
return newModelError(ErrNoLinkOrNoManagerOrFO, err)
}
return parseDBError(err)
}

func (this CorporationManagerResetPassword) getCorporationManager(linkID, email string) (*dbmodels.CorporationManagerCheckResult, IModelError) {
v, err := dbmodels.GetDB().GetCorporationManager(linkID, email)
if err == nil {
return v, nil
}

if err.IsErrorOf(dbmodels.ErrNoDBRecord) {
return v, newModelError(ErrNoLink, err)
}

return v, parseDBError(err)
}

func ListCorporationManagers(linkID, email, role string) ([]dbmodels.CorporationManagerListResult, IModelError) {
v, err := dbmodels.GetDB().ListCorporationManager(linkID, email, role)
if err == nil {
Expand All @@ -96,3 +151,7 @@ func ListCorporationManagers(linkID, email, role string) ([]dbmodels.Corporation

return v, parseDBError(err)
}

func newPWForCorpManager() string {
return util.RandStr(8, "alphanum")
}
18 changes: 14 additions & 4 deletions models/employee-manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,24 @@ func (this *EmployeeManagerCreateOption) ValidateWhenAdding(linkID, adminEmail s
}

func (this *EmployeeManagerCreateOption) Create(linkID string) ([]dbmodels.CorporationManagerCreateOption, IModelError) {
pws := map[string]string{}
opt := make([]dbmodels.CorporationManagerCreateOption, 0, len(this.Managers))

for i := range this.Managers {
item := &this.Managers[i]
pw := util.RandStr(8, "alphanum")

pw := newPWForCorpManager()
encryptedPW, merr := encryptPassword(pw)
if merr != nil {
return nil, merr
}

pws[item.Email] = pw
opt = append(opt, dbmodels.CorporationManagerCreateOption{
ID: item.ID,
Name: item.Name,
Email: item.Email,
Password: pw,
Password: encryptedPW,
Role: dbmodels.RoleManager,
})
}
Expand All @@ -101,9 +108,12 @@ func (this *EmployeeManagerCreateOption) Create(linkID string) ([]dbmodels.Corpo

es := util.EmailSuffix(opt[0].Email)
for i := range opt {
if opt[i].ID != "" {
opt[i].ID = fmt.Sprintf("%s_%s", opt[i].ID, es)
item := &opt[i]

if item.ID != "" {
item.ID = fmt.Sprintf("%s_%s", item.ID, es)
}
item.Password = pws[item.Email]
}
return opt, nil
}
Expand Down
6 changes: 5 additions & 1 deletion models/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@ const (
ErrNoLinkOrUnsigned ModelErrCode = "no_link_or_unsigned"
ErrUnsigned ModelErrCode = "unsigned"
ErrSamePassword ModelErrCode = "same_password"
ErrNoLinkOrNoManager ModelErrCode = "no_link_or_no_manager"
ErrWrongOldPassword ModelErrCode = "wrong_old_password"
ErrTooShortOrLongPassword ModelErrCode = "too_short_or_long_password"
ErrInvalidPassword ModelErrCode = "invalid_password"
ErrNoLinkOrNoManagerOrFO ModelErrCode = "no_link_or_no_manager_or_frequent_operation"
ErrNoLinkOrManagerExists ModelErrCode = "no_link_or_manager_exists"
ErrCorpManagerExists ModelErrCode = "corp_manager_exists"
ErrCorpManagerDoesNotExist ModelErrCode = "corp_manager_does_not_exist"
ErrInvalidManagerID ModelErrCode = "invalid_manager_id"
ErrDuplicateManagerID ModelErrCode = "duplicate_manager_id"
ErrEmptyPayload ModelErrCode = "empty_payload"
Expand Down
23 changes: 23 additions & 0 deletions models/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package models
import (
"fmt"
"regexp"

"golang.org/x/crypto/bcrypt"
)

func checkEmailFormat(email string) IModelError {
Expand All @@ -20,3 +22,24 @@ func checkManagerID(mid string) IModelError {
}
return nil
}

func checkPassword(s string) IModelError {
rg := regexp.MustCompile("^[\x21-\x7E]+$")
if !rg.MatchString(s) {
return newModelError(ErrInvalidPassword, fmt.Errorf("invalid manager id:%s", s))
}
return nil
}

func encryptPassword(pwd string) (string, IModelError) {
hash, err := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.MinCost)
if err != nil {
return "", newModelError(ErrSystemError, err)
}

return string(hash), nil
}

func isSamePasswords(hashedPwd, plainPwd string) bool {
return bcrypt.CompareHashAndPassword([]byte(hashedPwd), []byte(plainPwd)) == nil
}
Loading

0 comments on commit e419ef6

Please sign in to comment.