diff --git a/api/controller/contact.go b/api/controller/contact.go index d829343b9..4874624be 100644 --- a/api/controller/contact.go +++ b/api/controller/contact.go @@ -57,10 +57,6 @@ func CreateContact( userLogin, teamID string, ) *api.ErrorResponse { - if userLogin != "" && teamID != "" { - return api.ErrorInternalServer(fmt.Errorf("CreateContact: cannot create contact when both userLogin and teamID specified")) - } - if !isAllowedToUseContactType(auth, userLogin, contact.Type) { return api.ErrorInvalidRequest(ErrNotAllowedContactType) } @@ -117,12 +113,20 @@ func UpdateContact( contactData.Type = contactDTO.Type contactData.Value = contactDTO.Value contactData.Name = contactDTO.Name + + if contactDTO.User != "" || contactDTO.TeamID != "" { + contactData.User = contactDTO.User + contactData.Team = contactDTO.TeamID + } + if err := dataBase.SaveContact(&contactData); err != nil { return contactDTO, api.ErrorInternalServer(err) } + contactDTO.User = contactData.User contactDTO.TeamID = contactData.Team contactDTO.ID = contactData.ID + return contactDTO, nil } @@ -221,9 +225,11 @@ func CheckUserPermissionsForContact( } return moira.ContactData{}, api.ErrorInternalServer(err) } + if auth.IsAdmin(userLogin) { return contactData, nil } + if contactData.Team != "" { teamContainsUser, err := dataBase.IsTeamContainUser(contactData.Team, userLogin) if err != nil { @@ -233,9 +239,11 @@ func CheckUserPermissionsForContact( return contactData, nil } } + if contactData.User == userLogin { return contactData, nil } + return moira.ContactData{}, api.ErrorForbidden("you are not permitted") } diff --git a/api/controller/contact_test.go b/api/controller/contact_test.go index 961deba9f..8c398b528 100644 --- a/api/controller/contact_test.go +++ b/api/controller/contact_test.go @@ -366,15 +366,6 @@ func TestCreateContact(t *testing.T) { }) }) }) - - Convey("Error on create with both: userLogin and teamID specified", t, func() { - contact := &dto.Contact{ - Value: contactValue, - Type: contactType, - } - err := CreateContact(dataBase, auth, contact, userLogin, teamID) - So(err, ShouldResemble, api.ErrorInternalServer(fmt.Errorf("CreateContact: cannot create contact when both userLogin and teamID specified"))) - }) } func TestAdminsCreatesContact(t *testing.T) { @@ -504,6 +495,30 @@ func TestUpdateContact(t *testing.T) { So(expectedContact.Name, ShouldResemble, contactDTO.Name) }) + Convey("Success with rewrite user", func() { + newUser := "testUser" + contactDTO := dto.Contact{ + Value: contactValue, + Name: "some-name", + Type: contactType, + User: newUser, + } + contactID := uuid.Must(uuid.NewV4()).String() + contact := moira.ContactData{ + Value: contactDTO.Value, + Type: contactDTO.Type, + Name: contactDTO.Name, + ID: contactID, + User: newUser, + } + dataBase.EXPECT().SaveContact(&contact).Return(nil) + expectedContact, err := UpdateContact(dataBase, auth, contactDTO, moira.ContactData{ID: contactID, User: userLogin}) + So(err, ShouldBeNil) + So(expectedContact.User, ShouldResemble, newUser) + So(expectedContact.ID, ShouldResemble, contactID) + So(expectedContact.Name, ShouldResemble, contactDTO.Name) + }) + Convey("Error update not allowed contact", func() { contactDTO := dto.Contact{ Value: contactValue, @@ -587,6 +602,27 @@ func TestUpdateContact(t *testing.T) { So(expectedContact.ID, ShouldResemble, contactID) }) + Convey("Success with rewrite team", func() { + newTeam := "testTeam" + contactDTO := dto.Contact{ + Value: contactValue, + Type: contactType, + TeamID: newTeam, + } + contactID := uuid.Must(uuid.NewV4()).String() + contact := moira.ContactData{ + Value: contactDTO.Value, + Type: contactDTO.Type, + ID: contactID, + Team: newTeam, + } + dataBase.EXPECT().SaveContact(&contact).Return(nil) + expectedContact, err := UpdateContact(dataBase, auth, contactDTO, moira.ContactData{ID: contactID, Team: teamID}) + So(err, ShouldBeNil) + So(expectedContact.TeamID, ShouldResemble, newTeam) + So(expectedContact.ID, ShouldResemble, contactID) + }) + Convey("Error save", func() { contactDTO := dto.Contact{ Value: contactValue, diff --git a/api/controller/subscription.go b/api/controller/subscription.go index be6754307..c9fceec6c 100644 --- a/api/controller/subscription.go +++ b/api/controller/subscription.go @@ -81,13 +81,16 @@ func GetSubscription(dataBase moira.Database, subscriptionID string) (*dto.Subsc // UpdateSubscription updates existing subscription. func UpdateSubscription(dataBase moira.Database, subscriptionID string, userLogin string, subscription *dto.Subscription) *api.ErrorResponse { subscription.ID = subscriptionID - if subscription.TeamID == "" { + if subscription.TeamID == "" && subscription.User == "" { subscription.User = userLogin } + data := moira.SubscriptionData(*subscription) + if err := dataBase.SaveSubscription(&data); err != nil { return api.ErrorInternalServer(err) } + return nil } @@ -131,21 +134,26 @@ func CheckUserPermissionsForSubscription( } return moira.SubscriptionData{}, api.ErrorInternalServer(err) } + if auth.IsAdmin(userLogin) { return subscription, nil } + if subscription.TeamID != "" { teamContainsUser, err := dataBase.IsTeamContainUser(subscription.TeamID, userLogin) if err != nil { return moira.SubscriptionData{}, api.ErrorInternalServer(err) } + if teamContainsUser { return subscription, nil } } + if subscription.User == userLogin { return subscription, nil } + return moira.SubscriptionData{}, api.ErrorForbidden("you are not permitted") } diff --git a/api/dto/contact.go b/api/dto/contact.go index 84ffe2f57..376b190c5 100644 --- a/api/dto/contact.go +++ b/api/dto/contact.go @@ -36,5 +36,8 @@ func (contact *Contact) Bind(r *http.Request) error { if contact.Value == "" { return fmt.Errorf("contact value of type %s can not be empty", contact.Type) } + if contact.User != "" && contact.TeamID != "" { + return fmt.Errorf("contact cannot have both the user field and the team_id field filled in") + } return nil } diff --git a/api/dto/subscription.go b/api/dto/subscription.go index 38178db3a..ebbe7f0a9 100644 --- a/api/dto/subscription.go +++ b/api/dto/subscription.go @@ -84,6 +84,7 @@ func (subscription *Subscription) checkContacts(request *http.Request) error { if subscription.User != "" && teamID != "" { return ErrSubscriptionContainsTeamAndUser{} } + var contactIDs []string var err error if teamID != "" { @@ -112,6 +113,7 @@ func (subscription *Subscription) checkContacts(request *http.Request) error { if err != nil { return ErrProvidedContactsForbidden{contactIds: subscriptionContactIDs} } + anotherUserContactValues := make([]string, 0) anotherUserContactIDs := make([]string, 0) for i, contact := range contacts { @@ -121,11 +123,13 @@ func (subscription *Subscription) checkContacts(request *http.Request) error { anotherUserContactValues = append(anotherUserContactValues, contact.Value) } } + return ErrProvidedContactsForbidden{ contactNames: anotherUserContactValues, contactIds: subscriptionContactIDs, } } + return nil } diff --git a/api/handler/contact_test.go b/api/handler/contact_test.go index 6f469b23b..cc2ebc028 100644 --- a/api/handler/contact_test.go +++ b/api/handler/contact_test.go @@ -358,8 +358,8 @@ func TestCreateNewContact(t *testing.T) { }() expected := &api.ErrorResponse{ - StatusText: "Internal Server Error", - ErrorText: "CreateContact: cannot create contact when both userLogin and teamID specified", + StatusText: "Invalid request", + ErrorText: "contact cannot have both the user field and the team_id field filled in", } jsonContact, err := json.Marshal(newContactDto) So(err, ShouldBeNil) @@ -381,7 +381,7 @@ func TestCreateNewContact(t *testing.T) { So(err, ShouldBeNil) So(actual, ShouldResemble, expected) - So(response.StatusCode, ShouldEqual, http.StatusInternalServerError) + So(response.StatusCode, ShouldEqual, http.StatusBadRequest) }) }) } @@ -450,6 +450,51 @@ func TestUpdateContact(t *testing.T) { So(response.StatusCode, ShouldEqual, http.StatusOK) }) + Convey("Failed to update a contact with the specified user and team field", func() { + updatedContactDto.TeamID = defaultTeamID + defer func() { + updatedContactDto.TeamID = "" + }() + + jsonContact, err := json.Marshal(updatedContactDto) + So(err, ShouldBeNil) + + testRequest := httptest.NewRequest(http.MethodPut, "/contact/"+contactID, bytes.NewBuffer(jsonContact)) + + testRequest = testRequest.WithContext(middleware.SetContextValueForTest(testRequest.Context(), ContactKey, moira.ContactData{ + ID: contactID, + Name: updatedContactDto.Name, + Type: updatedContactDto.Type, + Value: updatedContactDto.Value, + User: updatedContactDto.User, + })) + testRequest = testRequest.WithContext(middleware.SetContextValueForTest(testRequest.Context(), AuthKey, &api.Authorization{ + AllowedContactTypes: map[string]struct{}{ + updatedContactDto.Type: {}, + }, + })) + + testRequest.Header.Add("content-type", "application/json") + + updateContact(responseWriter, testRequest) + + expected := &api.ErrorResponse{ + StatusText: "Invalid request", + ErrorText: "contact cannot have both the user field and the team_id field filled in", + } + response := responseWriter.Result() + defer response.Body.Close() + contentBytes, err := io.ReadAll(response.Body) + So(err, ShouldBeNil) + contents := string(contentBytes) + actual := &api.ErrorResponse{} + err = json.Unmarshal([]byte(contents), actual) + So(err, ShouldBeNil) + + So(actual, ShouldResemble, expected) + So(response.StatusCode, ShouldEqual, http.StatusBadRequest) + }) + Convey("Internal error when trying to update contact", func() { expected := &api.ErrorResponse{ StatusText: "Internal Server Error",