From 05807f9bac40ced8ba45b22797a1bf3bba1e54da Mon Sep 17 00:00:00 2001 From: Thiago Figueiredo Date: Thu, 30 Jul 2020 11:47:07 -0300 Subject: [PATCH 1/6] [cms] Fix setting of empty user owned proposals and supervisors --- politeiawww/user/cockroachdb/cms.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/politeiawww/user/cockroachdb/cms.go b/politeiawww/user/cockroachdb/cms.go index 3b732d1b2..5f4d0e48d 100644 --- a/politeiawww/user/cockroachdb/cms.go +++ b/politeiawww/user/cockroachdb/cms.go @@ -195,10 +195,10 @@ func (c *cockroachdb) updateCMSUser(tx *gorm.DB, nu user.UpdateCMSUser) error { if nu.ContractorContact != "" { cms.ContractorContact = nu.ContractorContact } - if superVisorUserIds != "" { + if superVisorUserIds != "" || cms.SupervisorUserID != superVisorUserIds { cms.SupervisorUserID = superVisorUserIds } - if proposalsOwned != "" { + if proposalsOwned != "" || cms.ProposalsOwned != proposalsOwned { cms.ProposalsOwned = proposalsOwned } From af41aec09a22510ac18b7aece407188aa70bf051 Mon Sep 17 00:00:00 2001 From: Thiago Figueiredo Date: Fri, 31 Jul 2020 17:26:38 -0300 Subject: [PATCH 2/6] fix cmswww manageuser proposalsowned and supervisorsid field setting --- politeiawww/cmd/cmswww/manageuser.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/politeiawww/cmd/cmswww/manageuser.go b/politeiawww/cmd/cmswww/manageuser.go index 548108c32..a243f88f8 100644 --- a/politeiawww/cmd/cmswww/manageuser.go +++ b/politeiawww/cmd/cmswww/manageuser.go @@ -43,12 +43,20 @@ func (cmd *CMSManageUserCmd) Execute(args []string) error { "revoked": cms.ContractorTypeRevoked, } + userID := cmd.Args.UserID + // Validate user ID - _, err := uuid.Parse(cmd.Args.UserID) + _, err := uuid.Parse(userID) if err != nil { return fmt.Errorf("invalid user ID: %v", err) } + // Retrieve user details + details, err := client.CMSUserDetails(strings.TrimSpace(userID)) + if err != nil { + return err + } + // Validate domain. The domain can be either the numeric code // or the human readable equivalent. var domain cms.DomainTypeT @@ -95,12 +103,18 @@ func (cmd *CMSManageUserCmd) Execute(args []string) error { return fmt.Errorf("invalid supervisor ID '%v': %v", v, err) } } + } else { + // Persist the value from the previous user details + supervisorIDs = details.User.SupervisorUserIDs } // Validate supervisor user IDs proposalsOwned := make([]string, 0, 16) if cmd.ProposalsOwned != "" { proposalsOwned = strings.Split(cmd.ProposalsOwned, ",") + } else { + // Persist the value from the previous user details + proposalsOwned = details.User.ProposalsOwned } // Send request From b09a154a051b6f0f1aa4df936fcbc5397de2a267 Mon Sep 17 00:00:00 2001 From: Thiago Figueiredo Date: Wed, 5 Aug 2020 10:01:35 -0300 Subject: [PATCH 3/6] Add ManageSupervisors and ManageProposalsOwned types for handling manage user updates --- politeiawww/api/cms/v1/v1.go | 28 ++++++++++--- politeiawww/cmd/cmswww/manageuser.go | 38 ++++++++++-------- politeiawww/cmsuser.go | 60 ++++++++++++++++++++++------ 3 files changed, 92 insertions(+), 34 deletions(-) diff --git a/politeiawww/api/cms/v1/v1.go b/politeiawww/api/cms/v1/v1.go index 627e04f44..c8b8c014f 100644 --- a/politeiawww/api/cms/v1/v1.go +++ b/politeiawww/api/cms/v1/v1.go @@ -17,6 +17,7 @@ type ContractorTypeT int type DCCTypeT int type DCCStatusT int type DCCVoteStatusT int +type ManageUserActionT int const ( APIVersion = 1 @@ -112,6 +113,11 @@ const ( DCCVoteStatusStarted DCCVoteStatusT = 2 DCCVoteStatusFinished DCCVoteStatusT = 3 + // Manage user available actions + Nothing ManageUserActionT = 0 + Set ManageUserActionT = 1 + Reset ManageUserActionT = 2 + InvoiceInputVersion = 1 // PolicyMaxImages is the maximum number of images accepted @@ -235,6 +241,7 @@ const ( ErrorStatusDCCVoteEnded www.ErrorStatusT = 1054 ErrorStatusDCCVoteStillLive www.ErrorStatusT = 1055 ErrorStatusDCCDuplicateVote www.ErrorStatusT = 1056 + ErrorStatusInvalidManageUserAction www.ErrorStatusT = 1057 ProposalsMainnet = "https://proposals.decred.org" ProposalsTestnet = "https://test-proposals.decred.org" @@ -374,6 +381,7 @@ var ( ErrorStatusDCCVoteEnded: "the all contractor voting period has ended", ErrorStatusDCCVoteStillLive: "cannot update status of a DCC while a vote is still live", ErrorStatusDCCDuplicateVote: "user has already submitted a vote for the given dcc", + ErrorStatusInvalidManageUserAction: "the client selected an invalid manage user action", } ) @@ -700,13 +708,23 @@ type EditUser struct { // EditUserReply is the reply for the EditUser command. type EditUserReply struct{} +type CMSManageSupervisors struct { + Action ManageUserActionT // Set or reset + Payload []string +} + +type CMSManageProposalsOwned struct { + Action ManageUserActionT // Set or reset + Payload []string +} + // CMSManageUser updates the various fields for a given user. type CMSManageUser struct { - UserID string `json:"userid"` - Domain DomainTypeT `json:"domain,omitempty"` - ContractorType ContractorTypeT `json:"contractortype,omitempty"` - SupervisorUserIDs []string `json:"supervisoruserids,omitempty"` - ProposalsOwned []string `json:"proposalsowned,omitempty"` + UserID string `json:"userid"` + Domain DomainTypeT `json:"domain,omitempty"` + ContractorType ContractorTypeT `json:"contractortype,omitempty"` + SupervisorUserIDs CMSManageSupervisors `json:"supervisoruserids,omitempty"` + ProposalsOwned CMSManageProposalsOwned `json:"proposalsowned,omitempty"` } // CMSManageUserReply is the reply for the CMSManageUserReply command. diff --git a/politeiawww/cmd/cmswww/manageuser.go b/politeiawww/cmd/cmswww/manageuser.go index a243f88f8..6c63e86ef 100644 --- a/politeiawww/cmd/cmswww/manageuser.go +++ b/politeiawww/cmd/cmswww/manageuser.go @@ -51,11 +51,11 @@ func (cmd *CMSManageUserCmd) Execute(args []string) error { return fmt.Errorf("invalid user ID: %v", err) } - // Retrieve user details - details, err := client.CMSUserDetails(strings.TrimSpace(userID)) - if err != nil { - return err - } + // // Retrieve user details + // details, err := client.CMSUserDetails(strings.TrimSpace(userID)) + // if err != nil { + // return err + // } // Validate domain. The domain can be either the numeric code // or the human readable equivalent. @@ -94,8 +94,13 @@ func (cmd *CMSManageUserCmd) Execute(args []string) error { } // Validate supervisor user IDs + var manageSupervisors cms.CMSManageSupervisors supervisorIDs := make([]string, 0, 16) - if cmd.SupervisorUserIDs != "" { + if cmd.SupervisorUserIDs == "" { + manageSupervisors.Action = cms.Nothing + } else if cmd.SupervisorUserIDs == "reset" { + manageSupervisors.Action = cms.Reset + } else { supervisorIDs = strings.Split(cmd.SupervisorUserIDs, ",") for _, v := range supervisorIDs { _, err := uuid.Parse(v) @@ -103,18 +108,19 @@ func (cmd *CMSManageUserCmd) Execute(args []string) error { return fmt.Errorf("invalid supervisor ID '%v': %v", v, err) } } - } else { - // Persist the value from the previous user details - supervisorIDs = details.User.SupervisorUserIDs + manageSupervisors.Action = cms.Set + manageSupervisors.Payload = supervisorIDs } // Validate supervisor user IDs - proposalsOwned := make([]string, 0, 16) - if cmd.ProposalsOwned != "" { - proposalsOwned = strings.Split(cmd.ProposalsOwned, ",") + var manageProposalsOwned cms.CMSManageProposalsOwned + if cmd.ProposalsOwned == "" { + manageProposalsOwned.Action = cms.Nothing + } else if cmd.ProposalsOwned == "reset" { + manageProposalsOwned.Action = cms.Reset } else { - // Persist the value from the previous user details - proposalsOwned = details.User.ProposalsOwned + manageProposalsOwned.Action = cms.Set + manageProposalsOwned.Payload = strings.Split(cmd.ProposalsOwned, ",") } // Send request @@ -122,8 +128,8 @@ func (cmd *CMSManageUserCmd) Execute(args []string) error { UserID: cmd.Args.UserID, Domain: domain, ContractorType: contractorType, - SupervisorUserIDs: supervisorIDs, - ProposalsOwned: proposalsOwned, + SupervisorUserIDs: manageSupervisors, + ProposalsOwned: manageProposalsOwned, } err = shared.PrintJSON(mu) if err != nil { diff --git a/politeiawww/cmsuser.go b/politeiawww/cmsuser.go index fd8ea59d8..cd43e2d37 100644 --- a/politeiawww/cmsuser.go +++ b/politeiawww/cmsuser.go @@ -362,40 +362,74 @@ func (p *politeiawww) processManageCMSUser(mu cms.CMSManageUser) (*cms.CMSManage if mu.ContractorType != 0 { uu.ContractorType = int(mu.ContractorType) } - if len(mu.SupervisorUserIDs) > 0 { - // Validate SupervisorUserID input - parseSuperUserIds := make([]uuid.UUID, 0, len(mu.SupervisorUserIDs)) - for _, super := range mu.SupervisorUserIDs { - parseUUID, err := uuid.Parse(super) + + previousCMSUser, err := p.getCMSUserByIDRaw(mu.UserID) + if err != nil { + return nil, err + } + + // Manage a user's supervisors + switch mu.SupervisorUserIDs.Action { + case cms.Set: + supervisors := mu.SupervisorUserIDs.Payload + parseSupervisors := make([]uuid.UUID, 0, len(supervisors)) + for _, id := range supervisors { + // Check if uuid is valid + parseUUID, err := uuid.Parse(id) if err != nil { - e := fmt.Sprintf("invalid uuid: %v", super) + e := fmt.Sprintf("invalid uuid: %v", id) return nil, www.UserError{ ErrorCode: cms.ErrorStatusInvalidSupervisorUser, ErrorContext: []string{e}, } } - u, err := p.getCMSUserByID(super) + // Check if user exists + u, err := p.getCMSUserByID(id) if err != nil { - e := fmt.Sprintf("user not found: %v", super) + e := fmt.Sprintf("user not found: %v", id) return nil, www.UserError{ ErrorCode: cms.ErrorStatusInvalidSupervisorUser, ErrorContext: []string{e}, } } + // Make sure the user is a supervisor if u.ContractorType != cms.ContractorTypeSupervisor { - e := fmt.Sprintf("user not a supervisor: %v", super) + e := fmt.Sprintf("user not a supervisor: %v", id) return nil, www.UserError{ ErrorCode: cms.ErrorStatusInvalidSupervisorUser, ErrorContext: []string{e}, } } - parseSuperUserIds = append(parseSuperUserIds, parseUUID) + parseSupervisors = append(parseSupervisors, parseUUID) + + } + uu.SupervisorUserIDs = parseSupervisors + case cms.Reset: + uu.SupervisorUserIDs = []uuid.UUID{} + case cms.Nothing: + uu.SupervisorUserIDs = previousCMSUser.SupervisorUserIDs + default: + e := fmt.Sprintf("invalid action: %v", mu.SupervisorUserIDs.Action) + return nil, www.UserError{ + ErrorCode: cms.ErrorStatusInvalidManageUserAction, + ErrorContext: []string{e}, } - uu.SupervisorUserIDs = parseSuperUserIds } - if len(mu.ProposalsOwned) > 0 { - uu.ProposalsOwned = mu.ProposalsOwned + // Manage a user's owned proposal + switch mu.ProposalsOwned.Action { + case cms.Set: + uu.ProposalsOwned = mu.ProposalsOwned.Payload + case cms.Reset: + uu.ProposalsOwned = []string{} + case cms.Nothing: + uu.ProposalsOwned = previousCMSUser.ProposalsOwned + default: + e := fmt.Sprintf("invalid action: %v", mu.SupervisorUserIDs.Action) + return nil, www.UserError{ + ErrorCode: cms.ErrorStatusInvalidManageUserAction, + ErrorContext: []string{e}, + } } payload, err := user.EncodeUpdateCMSUser(uu) From 66dc6150febb5c0c0a9875513e8c1306a699a75d Mon Sep 17 00:00:00 2001 From: Thiago Figueiredo Date: Wed, 12 Aug 2020 18:41:02 -0300 Subject: [PATCH 4/6] keep actions as string instead of its own type; code cleanup --- politeiawww/api/cms/v1/v1.go | 36 +++++++++---------- politeiawww/cmd/cmswww/manageuser.go | 54 ++++++++++++++++------------ politeiawww/cmsuser.go | 28 ++++++++++----- politeiawww/cmswww.go | 1 + 4 files changed, 70 insertions(+), 49 deletions(-) diff --git a/politeiawww/api/cms/v1/v1.go b/politeiawww/api/cms/v1/v1.go index c8b8c014f..ae56565a3 100644 --- a/politeiawww/api/cms/v1/v1.go +++ b/politeiawww/api/cms/v1/v1.go @@ -17,7 +17,6 @@ type ContractorTypeT int type DCCTypeT int type DCCStatusT int type DCCVoteStatusT int -type ManageUserActionT int const ( APIVersion = 1 @@ -113,11 +112,6 @@ const ( DCCVoteStatusStarted DCCVoteStatusT = 2 DCCVoteStatusFinished DCCVoteStatusT = 3 - // Manage user available actions - Nothing ManageUserActionT = 0 - Set ManageUserActionT = 1 - Reset ManageUserActionT = 2 - InvoiceInputVersion = 1 // PolicyMaxImages is the maximum number of images accepted @@ -325,6 +319,14 @@ var ( }, } + // PolicyCMSManageUserActions defines the actions allowed to edit the + // user fields on manage user route + PolicyCMSManageUserActions = []string{ + "", // Do nothing + "set", // Set payload + "reset", // Reset + } + // ErrorStatus converts error status codes to human readable text. ErrorStatus = map[www.ErrorStatusT]string{ ErrorStatusMalformedName: "malformed name", @@ -537,6 +539,7 @@ type PolicyReply struct { CMSStatementSupportedChars []string `json:"cmsstatementsupportedchars"` CMSSupportedLineItemTypes []AvailableLineItemType `json:"supportedlineitemtypes"` CMSSupportedDomains []AvailableDomain `json:"supporteddomains"` + CMSManageUserActions []string `json:"cmsmanageuseractions"` } // UserInvoices is used to get all of the invoices by userID. @@ -708,23 +711,20 @@ type EditUser struct { // EditUserReply is the reply for the EditUser command. type EditUserReply struct{} -type CMSManageSupervisors struct { - Action ManageUserActionT // Set or reset - Payload []string -} - -type CMSManageProposalsOwned struct { - Action ManageUserActionT // Set or reset +// CMSManageUserAction specifies commands that can be used to manage +// a user field. +type CMSManageUserAction struct { + Action string // Set or reset Payload []string } // CMSManageUser updates the various fields for a given user. type CMSManageUser struct { - UserID string `json:"userid"` - Domain DomainTypeT `json:"domain,omitempty"` - ContractorType ContractorTypeT `json:"contractortype,omitempty"` - SupervisorUserIDs CMSManageSupervisors `json:"supervisoruserids,omitempty"` - ProposalsOwned CMSManageProposalsOwned `json:"proposalsowned,omitempty"` + UserID string `json:"userid"` + Domain DomainTypeT `json:"domain,omitempty"` + ContractorType ContractorTypeT `json:"contractortype,omitempty"` + SupervisorUserIDs CMSManageUserAction `json:"supervisoruserids,omitempty"` + ProposalsOwned CMSManageUserAction `json:"proposalsowned,omitempty"` } // CMSManageUserReply is the reply for the CMSManageUserReply command. diff --git a/politeiawww/cmd/cmswww/manageuser.go b/politeiawww/cmd/cmswww/manageuser.go index 6c63e86ef..f11415ffc 100644 --- a/politeiawww/cmd/cmswww/manageuser.go +++ b/politeiawww/cmd/cmswww/manageuser.go @@ -5,10 +5,12 @@ package main import ( + "encoding/hex" "fmt" "strconv" "strings" + pd "github.com/decred/politeia/politeiad/api/v1" cms "github.com/decred/politeia/politeiawww/api/cms/v1" "github.com/decred/politeia/politeiawww/cmd/shared" "github.com/google/uuid" @@ -94,33 +96,41 @@ func (cmd *CMSManageUserCmd) Execute(args []string) error { } // Validate supervisor user IDs - var manageSupervisors cms.CMSManageSupervisors - supervisorIDs := make([]string, 0, 16) - if cmd.SupervisorUserIDs == "" { - manageSupervisors.Action = cms.Nothing - } else if cmd.SupervisorUserIDs == "reset" { - manageSupervisors.Action = cms.Reset - } else { - supervisorIDs = strings.Split(cmd.SupervisorUserIDs, ",") - for _, v := range supervisorIDs { - _, err := uuid.Parse(v) + var manageSupervisors cms.CMSManageUserAction + switch cmd.SupervisorUserIDs { + case "": + manageSupervisors.Action = cmd.SupervisorUserIDs + case "reset": + manageSupervisors.Action = cmd.SupervisorUserIDs + default: + ids := strings.Split(cmd.SupervisorUserIDs, ",") + for _, id := range ids { + _, err := uuid.Parse(id) if err != nil { - return fmt.Errorf("invalid supervisor ID '%v': %v", v, err) + return fmt.Errorf("invalid supervisor ID '%v': %v", id, err) } } - manageSupervisors.Action = cms.Set - manageSupervisors.Payload = supervisorIDs + manageSupervisors.Action = "set" + manageSupervisors.Payload = ids } - // Validate supervisor user IDs - var manageProposalsOwned cms.CMSManageProposalsOwned - if cmd.ProposalsOwned == "" { - manageProposalsOwned.Action = cms.Nothing - } else if cmd.ProposalsOwned == "reset" { - manageProposalsOwned.Action = cms.Reset - } else { - manageProposalsOwned.Action = cms.Set - manageProposalsOwned.Payload = strings.Split(cmd.ProposalsOwned, ",") + // Validate proposals owned + var manageProposalsOwned cms.CMSManageUserAction + switch cmd.ProposalsOwned { + case "": + manageProposalsOwned.Action = cmd.ProposalsOwned + case "reset": + manageProposalsOwned.Action = cmd.ProposalsOwned + default: + tokens := strings.Split(cmd.ProposalsOwned, ",") + for _, token := range tokens { + b, err := hex.DecodeString(token) + if err != nil || len(b) != pd.TokenSize { + return fmt.Errorf("invalid proposal token '%v': %v", token, err) + } + } + manageProposalsOwned.Action = "set" + manageProposalsOwned.Payload = tokens } // Send request diff --git a/politeiawww/cmsuser.go b/politeiawww/cmsuser.go index cd43e2d37..72d917cd3 100644 --- a/politeiawww/cmsuser.go +++ b/politeiawww/cmsuser.go @@ -8,6 +8,7 @@ import ( "strings" "time" + pd "github.com/decred/politeia/politeiad/api/v1" cms "github.com/decred/politeia/politeiawww/api/cms/v1" www "github.com/decred/politeia/politeiawww/api/www/v1" "github.com/decred/politeia/politeiawww/user" @@ -370,7 +371,7 @@ func (p *politeiawww) processManageCMSUser(mu cms.CMSManageUser) (*cms.CMSManage // Manage a user's supervisors switch mu.SupervisorUserIDs.Action { - case cms.Set: + case "set": supervisors := mu.SupervisorUserIDs.Payload parseSupervisors := make([]uuid.UUID, 0, len(supervisors)) for _, id := range supervisors { @@ -401,12 +402,11 @@ func (p *politeiawww) processManageCMSUser(mu cms.CMSManageUser) (*cms.CMSManage } } parseSupervisors = append(parseSupervisors, parseUUID) - } uu.SupervisorUserIDs = parseSupervisors - case cms.Reset: + case "reset": uu.SupervisorUserIDs = []uuid.UUID{} - case cms.Nothing: + case "": uu.SupervisorUserIDs = previousCMSUser.SupervisorUserIDs default: e := fmt.Sprintf("invalid action: %v", mu.SupervisorUserIDs.Action) @@ -418,14 +418,24 @@ func (p *politeiawww) processManageCMSUser(mu cms.CMSManageUser) (*cms.CMSManage // Manage a user's owned proposal switch mu.ProposalsOwned.Action { - case cms.Set: - uu.ProposalsOwned = mu.ProposalsOwned.Payload - case cms.Reset: + case "set": + tokens := mu.ProposalsOwned.Payload + for _, token := range tokens { + b, err := hex.DecodeString(token) + if err != nil || len(b) != pd.TokenSize { + return nil, www.UserError{ + ErrorCode: cms.ErrorStatusMalformedProposalToken, + ErrorContext: []string{token}, + } + } + } + uu.ProposalsOwned = tokens + case "reset": uu.ProposalsOwned = []string{} - case cms.Nothing: + case "": uu.ProposalsOwned = previousCMSUser.ProposalsOwned default: - e := fmt.Sprintf("invalid action: %v", mu.SupervisorUserIDs.Action) + e := fmt.Sprintf("invalid action: %v", mu.ProposalsOwned.Action) return nil, www.UserError{ ErrorCode: cms.ErrorStatusInvalidManageUserAction, ErrorContext: []string{e}, diff --git a/politeiawww/cmswww.go b/politeiawww/cmswww.go index f9f0cedb7..68c4ceda1 100644 --- a/politeiawww/cmswww.go +++ b/politeiawww/cmswww.go @@ -411,6 +411,7 @@ func (p *politeiawww) handleCMSPolicy(w http.ResponseWriter, r *http.Request) { CMSStatementSupportedChars: cms.PolicySponsorStatementSupportedChars, CMSSupportedDomains: cms.PolicySupportedCMSDomains, CMSSupportedLineItemTypes: cms.PolicyCMSSupportedLineItemTypes, + CMSManageUserActions: cms.PolicyCMSManageUserActions, } util.RespondWithJSON(w, http.StatusOK, reply) From b512e4e593f775196bbece96a790069197785d70 Mon Sep 17 00:00:00 2001 From: Thiago Figueiredo Date: Wed, 12 Aug 2020 18:49:05 -0300 Subject: [PATCH 5/6] cleanup --- politeiawww/cmd/cmswww/manageuser.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/politeiawww/cmd/cmswww/manageuser.go b/politeiawww/cmd/cmswww/manageuser.go index f11415ffc..944c345e9 100644 --- a/politeiawww/cmd/cmswww/manageuser.go +++ b/politeiawww/cmd/cmswww/manageuser.go @@ -53,12 +53,6 @@ func (cmd *CMSManageUserCmd) Execute(args []string) error { return fmt.Errorf("invalid user ID: %v", err) } - // // Retrieve user details - // details, err := client.CMSUserDetails(strings.TrimSpace(userID)) - // if err != nil { - // return err - // } - // Validate domain. The domain can be either the numeric code // or the human readable equivalent. var domain cms.DomainTypeT From 13036a9c219205e835a5b9d08c30a9a457caadf8 Mon Sep 17 00:00:00 2001 From: Thiago Figueiredo Date: Thu, 13 Aug 2020 08:37:20 -0300 Subject: [PATCH 6/6] cdb check not needed anymore --- politeiawww/user/cockroachdb/cms.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/politeiawww/user/cockroachdb/cms.go b/politeiawww/user/cockroachdb/cms.go index 5f4d0e48d..3b732d1b2 100644 --- a/politeiawww/user/cockroachdb/cms.go +++ b/politeiawww/user/cockroachdb/cms.go @@ -195,10 +195,10 @@ func (c *cockroachdb) updateCMSUser(tx *gorm.DB, nu user.UpdateCMSUser) error { if nu.ContractorContact != "" { cms.ContractorContact = nu.ContractorContact } - if superVisorUserIds != "" || cms.SupervisorUserID != superVisorUserIds { + if superVisorUserIds != "" { cms.SupervisorUserID = superVisorUserIds } - if proposalsOwned != "" || cms.ProposalsOwned != proposalsOwned { + if proposalsOwned != "" { cms.ProposalsOwned = proposalsOwned }