Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#7706] Platform: Edit backup config credentials. #8050

Merged
merged 28 commits into from
May 12, 2021
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
95b3138
[#7706] Platform: Set initial Values for the form.
nishantSharma459 Apr 1, 2021
ebc47af
Added edit API for customer config.
mahendranbhat Apr 2, 2021
1cd4f9a
Merge branch '7706-Edit-Configs' of github.com:jaydeepkumara/yugabyte…
mahendranbhat Apr 2, 2021
e792ba6
Test cases and code improvements
mahendranbhat Apr 4, 2021
e152b0d
[#7706] Platform: Integrated the API.
nishantSharma459 Apr 6, 2021
4f205b3
[#7706] Platform: Add Edit button and cancel button.
nishantSharma459 Apr 7, 2021
e750ef2
Merge branch 'master' of github.com:jaydeepkumara/yugabyte-db into 77…
mahendranbhat Apr 7, 2021
d847aac
[#7706] Platform: Hide the update button initially.
nishantSharma459 Apr 8, 2021
a5a7011
[#7706] Platform: Minor changes.
nishantSharma459 Apr 9, 2021
bd965bc
Merge branch 'master' of github.com:jaydeepkumara/yugabyte-db into 77…
nishantSharma459 Apr 15, 2021
cf27671
[#7706] Platform: removed unsed variables.
nishantSharma459 Apr 15, 2021
6d16016
review comment fixes
mahendranbhat Apr 18, 2021
f285d55
Merge branch '7706-Edit-Configs' of github.com:jaydeepkumara/yugabyte…
mahendranbhat Apr 18, 2021
5758038
[#7706] Platform: Fixed the issues related to the AWS config.
nishantSharma459 Apr 19, 2021
88e5728
Merge branch '7706-Edit-Configs' of github.com:jaydeepkumara/yugabyte…
nishantSharma459 Apr 19, 2021
9c0b652
[#7706] Platform: removed unsed variables.
nishantSharma459 Apr 20, 2021
5665ed5
code improvements
mahendranbhat Apr 20, 2021
76df1b8
[#7706] Platform: Fixed review comments.
nishantSharma459 Apr 21, 2021
2804e86
[#7706] Platform: Ran prettier.
nishantSharma459 Apr 21, 2021
3baf2c9
[#7706] Platform: Fixed validation on edit.
nishantSharma459 Apr 29, 2021
b9b6e46
review comment fixes
mahendranbhat Apr 30, 2021
cc7fdfe
Merge branch '7706-Edit-Configs' of github.com:jaydeepkumara/yugabyte…
mahendranbhat Apr 30, 2021
77ec4bd
review comment fixes
mahendranbhat May 5, 2021
ee6a95d
Merge branch 'master' of github.com:jaydeepkumara/yugabyte-db into 77…
mahendranbhat May 5, 2021
0df5750
[#7706] Platform: removed unsed variables.
nishantSharma459 May 6, 2021
fbbf385
Merge branch '7706-Edit-Configs' of github.com:jaydeepkumara/yugabyte…
nishantSharma459 May 6, 2021
0f32b7a
Merge branch 'master' of github.com:jaydeepkumara/yugabyte-db into 77…
mahendranbhat May 12, 2021
5c2cf0a
audit entry added
mahendranbhat May 12, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,26 @@

package com.yugabyte.yw.controllers;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import com.google.inject.Inject;

import com.yugabyte.yw.common.ApiResponse;
import com.yugabyte.yw.models.Audit;
import com.yugabyte.yw.models.CustomerConfig;
import com.yugabyte.yw.models.helpers.CommonUtils;
import com.yugabyte.yw.models.helpers.CustomerConfigValidator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import play.libs.Json;
import play.mvc.Result;

import java.util.UUID;


public class CustomerConfigController extends AuthenticatedController {
public static final Logger LOG = LoggerFactory.getLogger(CustomerConfigController.class);

Expand Down Expand Up @@ -57,4 +61,31 @@ public Result delete(UUID customerUUID, UUID configUUID) {
public Result list(UUID customerUUID) {
return ApiResponse.success(CustomerConfig.getAll(customerUUID));
}

public Result edit(UUID customerUUID, UUID configUUID) {
JsonNode formData = request().body().asJson();
ObjectNode errorJson = configValidator.validateFormData(formData);
if (errorJson.size() > 0) {
return ApiResponse.error(BAD_REQUEST, errorJson);
}

errorJson = configValidator.validateDataContent(formData);
if (errorJson.size() > 0) {
return ApiResponse.error(BAD_REQUEST, errorJson);
}
CustomerConfig customerConfig = CustomerConfig.get(customerUUID, configUUID);
if (customerConfig == null) {
return ApiResponse.error(BAD_REQUEST, "Invalid configUUID: " + configUUID);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid Customer configuration UUID.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consistent with the delete API response.

}
CustomerConfig config = CustomerConfig.get(configUUID);
JsonNode data = Json.toJson(formData.get("data"));
if (data != null && data.get("BACKUP_LOCATION") != null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please extract "BACKUP_LOCATION" into a constant or use CustomerConfigValidator.BACKUP_LOCATION_FIELDNAME.
P.S. BTW, maybe it is even better to extract these field names into a separate file if we going to use them not only inside CustomerConfigValidator.

Copy link
Contributor

@mahendranbhat mahendranbhat Apr 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are using the string instead of constants lot of places. Including other tests and utils file for masking. I will create another issue to fix these separately.

((ObjectNode)data).put("BACKUP_LOCATION", config.data.get("BACKUP_LOCATION"));
}
JsonNode updatedData = CommonUtils.unmaskConfig(config.data, data);
config.data = Json.toJson(updatedData);
config.update();
Audit.createAuditEntry(ctx(), request());
mahendranbhat marked this conversation as resolved.
Show resolved Hide resolved
return ApiResponse.success(config);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ public static JsonNode maskConfig(JsonNode config) {
}

private static String getMaskedValue(String key, String value) {
return isStrictlySensitiveField(key) || (value == null) ? MASKED_FIELD_VALUE
: value.replaceAll(maskRegex, "*");
return isStrictlySensitiveField(key) || value.length() < 5
|| (value == null) ? MASKED_FIELD_VALUE : value.replaceAll(maskRegex, "*");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No-no-no. If you have this in such variant then you don't have a test case for this. IIRC, boolean operations are executed from left to right order. So if you have value=null, you will get NPE here as value.length() < 5 is executed before (value == null).
Update here and update tests to control this case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops!! Yeah, did not check the last condition. Will update and add a test case too.

}

/**
Expand Down
1 change: 1 addition & 0 deletions managed/src/main/resources/v1.routes
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ DELETE /customers/:cUUID/providers/:pUUID/instance_types/:code c
POST /customers/:cUUID/providers/setup_docker com.yugabyte.yw.controllers.CloudProviderController.setupDocker(cUUID: java.util.UUID)
POST /customers/:cUUID/configs com.yugabyte.yw.controllers.CustomerConfigController.create(cUUID: java.util.UUID)
GET /customers/:cUUID/configs com.yugabyte.yw.controllers.CustomerConfigController.list(cUUID: java.util.UUID)
PUT /customers/:cUUID/configs/:configUUID com.yugabyte.yw.controllers.CustomerConfigController.edit(cUUID: java.util.UUID, configUUID: java.util.UUID)
DELETE /customers/:cUUID/configs/:configUUID com.yugabyte.yw.controllers.CustomerConfigController.delete(cUUID: java.util.UUID, configUUID: java.util.UUID)

#---------------------------------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,65 @@ public void testDeleteInUseStorageConfig() {
assertEquals(0, CustomerConfig.getAll(defaultCustomer.uuid).size());
assertAuditEntry(1, defaultCustomer.uuid);
}

@Test
public void testEditInUseStorageConfig() {
ObjectNode bodyJson = Json.newObject();
JsonNode data = Json.parse("{\"BACKUP_LOCATION\": \"test\", \"ACCESS_KEY\": \"A-KEY\", " +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Putting strings here instead of somewhere defined constants may give us some problems later if someone decides to rename one of these fields. I suggest to create/use Java constants.

Copy link
Contributor

@mahendranbhat mahendranbhat Apr 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we are using the string instead of constants lot of places. Including other tests and utils file for masking. I will create another issue to fix these separately.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, this works for me.

"\"ACCESS_SECRET\": \"A-SECRET\"}");
bodyJson.put("name", "test1");
bodyJson.set("data", data);
bodyJson.put("type", "STORAGE");
UUID configUUID = ModelFactory.createS3StorageConfig(defaultCustomer).configUUID;
Backup backup = ModelFactory.createBackup(defaultCustomer.uuid, UUID.randomUUID(),
configUUID);
String url = "/api/customers/" + defaultCustomer.uuid + "/configs/" + configUUID;
Result result = FakeApiHelper.doRequestWithAuthTokenAndBody("PUT", url,
defaultUser.createAuthToken(), bodyJson);
assertOk(result);
backup.delete();
result = FakeApiHelper.doRequestWithAuthTokenAndBody("PUT", url,
defaultUser.createAuthToken(), bodyJson);
assertOk(result);
JsonNode json = Json.parse(contentAsString(result));
assertEquals("s3://foo", json.get("data").get("BACKUP_LOCATION").textValue());
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Required to add more test cases.

  1. testcase to edit not used config. It should allow all fields to edit.
  2. testcase to edit in the used config should config with limited fields.
  3. In used config with all field edit. API should give an error.
  4. Invalid Data, Content, and Invalid configured. you can add 1 test case for each or add one testcase and cover all 3 senario.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jaydeepkumara for both used and unused configs, editable fields are the same I believe. ( at least the current implementation of UI behaves like that. @nishantSharma459 confirm )

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok.

@Test
public void testEditInvalidCustomerConfig() {
ObjectNode bodyJson = Json.newObject();
JsonNode data = Json.parse("{\"foo\":\"bar\"}");
bodyJson.put("name", "test1");
bodyJson.set("data", data);
bodyJson.put("type", "STORAGE");
Customer customer = ModelFactory.testCustomer("nc", "New Customer");
UUID configUUID = ModelFactory.createS3StorageConfig(customer).configUUID;
String url = "/api/customers/" + defaultCustomer.uuid + "/configs/" + configUUID;
Result result = FakeApiHelper.doRequestWithAuthTokenAndBody("PUT", url,
defaultUser.createAuthToken(), bodyJson);
assertBadRequest(result, "Invalid configUUID: " + configUUID);
assertEquals(1, CustomerConfig.getAll(customer.uuid).size());
assertAuditEntry(0, defaultCustomer.uuid);
}

@Test
public void testEditWithBackupLocation() {
ObjectNode bodyJson = Json.newObject();
JsonNode data = Json.parse("{\"BACKUP_LOCATION\": \"test\", \"ACCESS_KEY\": \"A-KEY-NEW\", " +
"\"ACCESS_SECRET\": \"A-SECRET-NEW\"}");
bodyJson.put("name", "test1");
bodyJson.set("data", data);
bodyJson.put("type", "STORAGE");
UUID configUUID = ModelFactory.createS3StorageConfig(defaultCustomer).configUUID;
String url = "/api/customers/" + defaultCustomer.uuid + "/configs/" + configUUID;
Result result = FakeApiHelper.doRequestWithAuthTokenAndBody("PUT", url,
defaultUser.createAuthToken(), bodyJson);
assertOk(result);
JsonNode json = Json.parse(contentAsString(result));
// Should not update the field BACKUP_LOCATION to "test".
assertEquals("s3://foo", json.get("data").get("BACKUP_LOCATION").textValue());
// SHould be updated and the API response should give asked data.
assertEquals("A-*****EW", json.get("data").get("ACCESS_KEY").textValue());
assertEquals("A-********EW", json.get("data").get("ACCESS_SECRET").textValue());
}
}
28 changes: 28 additions & 0 deletions managed/ui/src/actions/customers.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ export const FETCH_YUGAWARE_VERSION_RESPONSE = 'FETCH_YUGAWARE_VERSION_RESPONSE'
export const ADD_CUSTOMER_CONFIG = 'ADD_CUSTOMER_CONFIG';
export const ADD_CUSTOMER_CONFIG_RESPONSE = 'ADD_CUSTOMER_CONFIG_RESPONSE';

export const SET_INITIAL_CONFIG = 'SET_INITIAL_CONFIG';

export const UPDATE_CUSTOMER_CONFIG = 'UPDATE_CUSTOMER_CONFIG';
export const UPDATE_CUSTOMER_CONFIG_RESPONSE = 'UPDATE_CUSTOMER_CONFIG_RESPONSE';

export const DELETE_CUSTOMER_CONFIG = 'DELETE_CUSTOMER_CONFIG';
export const DELETE_CUSTOMER_CONFIG_RESPONSE = 'DELETE_CUSTOMER_CONFIG_RESPONSE';

Expand Down Expand Up @@ -488,6 +493,29 @@ export function addCustomerConfig(config) {
};
}

export function setInitialConfigValues(initialValues) {
return {
type: SET_INITIAL_CONFIG,
payload: initialValues
};
}

export function updateCustomerConfig(config) {
const cUUID = localStorage.getItem('customerId');
const request = axios.put(`${ROOT_URL}/customers/${cUUID}/configs/${config.configUUID}`, config);
return {
type: UPDATE_CUSTOMER_CONFIG,
payload: request
};
}

export function updateCustomerConfigResponse(response) {
return {
type: UPDATE_CUSTOMER_CONFIG_RESPONSE,
payload: response
};
}

export function addCustomerConfigResponse(response) {
return {
type: ADD_CUSTOMER_CONFIG_RESPONSE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -400,10 +400,10 @@
display: flex;
justify-content: flex-end;
align-items: center;
column-gap: 8px;

.disable-delete {
display: inline-block;
margin: 4px 20px 4px auto;
font-size: inherit;

.fa-ban {
Expand Down
Loading