diff --git a/spring-credhub/pom.xml b/spring-credhub/pom.xml new file mode 100644 index 0000000000..57fbe5c9d6 --- /dev/null +++ b/spring-credhub/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + com.baeldung.spring-credhub + spring-credhub + 1.0-SNAPSHOT + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../parent-boot-2 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.projectlombok + lombok + + + org.springframework.credhub + spring-credhub-starter + 2.2.0 + + + com.google.code.gson + gson + + + + + 8 + 8 + UTF-8 + + + \ No newline at end of file diff --git a/spring-credhub/src/main/java/com/baeldung/OrderApplication.java b/spring-credhub/src/main/java/com/baeldung/OrderApplication.java new file mode 100644 index 0000000000..94d34c18bb --- /dev/null +++ b/spring-credhub/src/main/java/com/baeldung/OrderApplication.java @@ -0,0 +1,7 @@ +package com.baeldung; + +public class OrderApplication { + public static void main(String[] args) { + System.out.println("Hello world!"); + } +} \ No newline at end of file diff --git a/spring-credhub/src/main/java/com/baeldung/controller/CredentialController.java b/spring-credhub/src/main/java/com/baeldung/controller/CredentialController.java new file mode 100644 index 0000000000..8da2b98dfd --- /dev/null +++ b/spring-credhub/src/main/java/com/baeldung/controller/CredentialController.java @@ -0,0 +1,55 @@ +package com.baeldung.controller; + +import com.baeldung.model.Credential; +import com.baeldung.service.CredentialService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.credhub.support.CredentialPermission; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class CredentialController { + @Autowired + CredentialService credentialService; + + @PostMapping("/credentials") + public ResponseEntity generatePassword(@RequestBody String credentialName) { + return new ResponseEntity<>(credentialService.generatePassword(credentialName), HttpStatus.OK); + } + + @PutMapping("/credentials") + public ResponseEntity writeJSONCredential(@RequestBody Credential secret) { + return new ResponseEntity<>(credentialService.writeCredential(secret), HttpStatus.OK); + } + + @PostMapping("/credentials/rotate") + public ResponseEntity rotatePassword(@RequestBody String credentialName) { + return new ResponseEntity<>(credentialService.rotatePassword(credentialName), HttpStatus.OK); + } + + @DeleteMapping("/credentials") + public ResponseEntity deletePassword(@RequestBody String credentialName) { + return new ResponseEntity<>(credentialService.deletePassword(credentialName), HttpStatus.OK); + } + + @GetMapping("/credentials") + public ResponseEntity getPassword(@RequestBody String credentialName) { + return new ResponseEntity<>(credentialService.getPassword(credentialName), HttpStatus.OK); + } + + @PostMapping("/permissions") + public ResponseEntity addPermission(@RequestBody String credentialName) { + return new ResponseEntity<>(credentialService.addCredentialPermission(credentialName), HttpStatus.OK); + } + + @GetMapping("/permissions") + public ResponseEntity getPermission(@RequestBody String credentialName) { + return new ResponseEntity<>(credentialService.getCredentialPermission(credentialName), HttpStatus.OK); + } +} diff --git a/spring-credhub/src/main/java/com/baeldung/controller/OrderController.java b/spring-credhub/src/main/java/com/baeldung/controller/OrderController.java new file mode 100644 index 0000000000..d839cd291b --- /dev/null +++ b/spring-credhub/src/main/java/com/baeldung/controller/OrderController.java @@ -0,0 +1,56 @@ +package com.baeldung.controller; + +import static java.time.LocalDate.now; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import com.baeldung.model.Order; +import com.baeldung.service.CredentialService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/orders") +public class OrderController { + + @Autowired + CredentialService credentialService; + + public OrderController(CredentialService credentialService) { + this.credentialService = credentialService; + } + + @GetMapping + public ResponseEntity> getAllOrders() { + try { + String apiKey = credentialService.getPassword("api_key"); + return new ResponseEntity<>(getOrderList(apiKey), HttpStatus.OK); + } catch (Exception e) { + return new ResponseEntity<>(HttpStatus.FORBIDDEN); + } + } + + private List getOrderList(String apiKey) throws Exception { + if (!credentialMatch(apiKey)) + throw new Exception(); + Order order = new Order(); + order.setId(123L); + order.setCustomerName("Craig"); + order.setOrderDate(now()); + List orderList = new ArrayList<>(); + orderList.add(order); + return orderList; + } + + private boolean credentialMatch(String credValue) { + //logic to check credValue + return true; + } +} + diff --git a/spring-credhub/src/main/java/com/baeldung/model/Credential.java b/spring-credhub/src/main/java/com/baeldung/model/Credential.java new file mode 100644 index 0000000000..c7fa25b261 --- /dev/null +++ b/spring-credhub/src/main/java/com/baeldung/model/Credential.java @@ -0,0 +1,14 @@ +package com.baeldung.model; + +import java.util.Map; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class Credential { + Map value; + String type; + String name; +} diff --git a/spring-credhub/src/main/java/com/baeldung/model/Order.java b/spring-credhub/src/main/java/com/baeldung/model/Order.java new file mode 100644 index 0000000000..31f40956ca --- /dev/null +++ b/spring-credhub/src/main/java/com/baeldung/model/Order.java @@ -0,0 +1,15 @@ +package com.baeldung.model; + +import java.time.LocalDate; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class Order { + + private Long id; + private String customerName; + private LocalDate orderDate; +} diff --git a/spring-credhub/src/main/java/com/baeldung/service/CredentialService.java b/spring-credhub/src/main/java/com/baeldung/service/CredentialService.java new file mode 100644 index 0000000000..75a11cdb9a --- /dev/null +++ b/spring-credhub/src/main/java/com/baeldung/service/CredentialService.java @@ -0,0 +1,195 @@ +package com.baeldung.service; + +import java.util.Map; +import java.util.UUID; + +import org.springframework.credhub.core.CredHubOperations; +import org.springframework.credhub.core.credential.CredHubCredentialOperations; +import org.springframework.credhub.core.permissionV2.CredHubPermissionV2Operations; +import org.springframework.credhub.support.CredentialDetails; +import org.springframework.credhub.support.CredentialPermission; +import org.springframework.credhub.support.CredentialRequest; +import org.springframework.credhub.support.SimpleCredentialName; +import org.springframework.credhub.support.certificate.CertificateCredential; +import org.springframework.credhub.support.certificate.CertificateCredentialRequest; +import org.springframework.credhub.support.json.JsonCredentialRequest; +import org.springframework.credhub.support.password.PasswordCredential; +import org.springframework.credhub.support.password.PasswordCredentialRequest; +import org.springframework.credhub.support.password.PasswordParameters; +import org.springframework.credhub.support.password.PasswordParametersRequest; +import org.springframework.credhub.support.permissions.Operation; +import org.springframework.credhub.support.permissions.Permission; +import org.springframework.credhub.support.rsa.RsaCredential; +import org.springframework.credhub.support.rsa.RsaCredentialRequest; +import org.springframework.credhub.support.ssh.SshCredential; +import org.springframework.credhub.support.ssh.SshCredentialRequest; +import org.springframework.credhub.support.user.UserCredential; +import org.springframework.credhub.support.user.UserCredentialRequest; +import org.springframework.credhub.support.value.ValueCredential; +import org.springframework.credhub.support.value.ValueCredentialRequest; + +import com.baeldung.model.Credential; + +public class CredentialService { + + private final CredHubCredentialOperations credentialOperations; + private final CredHubPermissionV2Operations permissionOperations; + + public CredentialService(CredHubOperations credHubOperations) { + this.credentialOperations = credHubOperations.credentials(); + this.permissionOperations = credHubOperations.permissionsV2(); + } + + public String generatePassword(String name) { + try { + SimpleCredentialName credentialName = new SimpleCredentialName(name); + PasswordParameters parameters = PasswordParameters.builder() + .length(24) + .excludeUpper(false) + .excludeLower(false) + .includeSpecial(true) + .excludeNumber(false) + .build(); + + CredentialDetails generatedCred = credentialOperations.generate(PasswordParametersRequest.builder() + .name(credentialName) + .parameters(parameters) + .build()); + + return generatedCred.getValue() + .getPassword(); + } catch (Exception e) { + return null; + } + } + + public String writeCredential(Credential credential) { + try { + SimpleCredentialName credentialName = new SimpleCredentialName(credential.getName()); + CredentialRequest request = null; + Map value = credential.getValue(); + switch (credential.getType()) { + case "value": + ValueCredential valueCredential = new ValueCredential((String) value.get("value")); + request = ValueCredentialRequest.builder() + .name(credentialName) + .value(valueCredential) + .build(); + break; + + case "json": + request = JsonCredentialRequest.builder() + .name(credentialName) + .value(value) + .build(); + break; + + case "user": + UserCredential userCredential = new UserCredential((String) value.get("username"), (String) value.get("password")); + request = UserCredentialRequest.builder() + .name(credentialName) + .value(userCredential) + .build(); + break; + + case "password": + PasswordCredential passwordCredential = new PasswordCredential((String) value.get("password")); + request = PasswordCredentialRequest.builder() + .name(credentialName) + .value(passwordCredential) + .build(); + break; + + case "certificate": + CertificateCredential certificateCredential = new CertificateCredential((String) value.get("certificate"), (String) value.get("certificate_authority"), (String) value.get("private_key")); + request = CertificateCredentialRequest.builder() + .name(credentialName) + .value(certificateCredential) + .build(); + break; + + case "rsa": + RsaCredential rsaCredential = new RsaCredential((String) value.get("public_key"), (String) value.get("private_key")); + request = RsaCredentialRequest.builder() + .name(credentialName) + .value(rsaCredential) + .build(); + break; + + case "ssh": + SshCredential sshCredential = new SshCredential((String) value.get("public_key"), (String) value.get("private_key")); + request = SshCredentialRequest.builder() + .name(credentialName) + .value(sshCredential) + .build(); + break; + + default: + } + if (request != null) { + credentialOperations.write(request); + } + return "Credential:" + credentialName + " written successfully!"; + } catch (Exception e) { + return "Error! Unable to write credential"; + } + } + + public String rotatePassword(String name) { + try { + SimpleCredentialName credentialName = new SimpleCredentialName(name); + CredentialDetails oldPassword = credentialOperations.getByName(credentialName, PasswordCredential.class); + CredentialDetails newPassword = credentialOperations.regenerate(credentialName, PasswordCredential.class); + + return "Credential:" + credentialName + " re-generated successfully!"; + } catch (Exception e) { + return "Error! Unable to re-generate credential"; + } + } + + public String deletePassword(String name) { + try { + SimpleCredentialName credentialName = new SimpleCredentialName(name); + credentialOperations.deleteByName(credentialName); + return "Credential:" + credentialName + " deleted successfully!"; + } catch (Exception e) { + return "Error! Unable to delete credential"; + } + } + + public String getPassword(String name) { + try { + SimpleCredentialName credentialName = new SimpleCredentialName(name); + return credentialOperations.getByName(credentialName, PasswordCredential.class) + .getValue() + .getPassword(); + } catch (Exception e) { + return null; + } + } + + public CredentialPermission addCredentialPermission(String name) { + SimpleCredentialName credentialName = new SimpleCredentialName(name); + try { + Permission permission = Permission.builder() + .app(UUID.randomUUID() + .toString()) + .operations(Operation.READ, Operation.WRITE) + .user("u101") + .build(); + CredentialPermission credentialPermission = permissionOperations.addPermissions(credentialName, permission); + return credentialPermission; + } catch (Exception e) { + return null; + } + } + + public CredentialPermission getCredentialPermission(String name) { + try { + return permissionOperations.getPermissions(name); + } catch (Exception e) { + return null; + } + } +} + diff --git a/spring-credhub/src/main/resources/application.yml b/spring-credhub/src/main/resources/application.yml new file mode 100644 index 0000000000..cd2519e4c5 --- /dev/null +++ b/spring-credhub/src/main/resources/application.yml @@ -0,0 +1,9 @@ +management: + endpoints: + web: + exposure: + include: "*" + +spring: + credhub: + url: \ No newline at end of file diff --git a/spring-credhub/src/test/java/com/baeldung/controller/CredentialServiceUnitTest.java b/spring-credhub/src/test/java/com/baeldung/controller/CredentialServiceUnitTest.java new file mode 100644 index 0000000000..cc785cabda --- /dev/null +++ b/spring-credhub/src/test/java/com/baeldung/controller/CredentialServiceUnitTest.java @@ -0,0 +1,87 @@ +package com.baeldung.controller; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.assertj.core.util.DateUtil; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.credhub.support.CredentialPermission; +import org.springframework.credhub.support.permissions.Operation; + +import com.baeldung.model.Credential; +import com.baeldung.service.CredentialService; + +@Ignore +@ExtendWith(MockitoExtension.class) +public class CredentialServiceUnitTest { + + @InjectMocks + private CredentialService credentialService; + + @Test + public void whenGeneratePassword_thenReturnNewPassword() { + String orderApiKey = credentialService.generatePassword("order_api_key"); + assertFalse(orderApiKey.isEmpty()); + } + + @Test + public void whenWriteCredential_thenReturnSuccess() { + Map value = new HashMap<>(); + value.put("end_date", DateUtil.now()); + value.put("start_date", DateUtil.yesterday()); + + Credential credential = new Credential(); + credential.setName("order_config_json"); + credential.setType("json"); + credential.setValue(value); + + String result = credentialService.writeCredential(credential); + assertThat(result).isEqualTo("Credential:order_config_json written successfully!"); + } + + @Test + public void whenRotatePassword_thenRegenerateNewPassword() { + String orderApiKey = credentialService.rotatePassword("order_api_key"); + assertThat(orderApiKey).isEqualTo("Credential:order_api_key re-generated successfully!"); + } + + @Test + public void whenRevokePassword_thenDeletePassword() { + String orderApiKey = credentialService.deletePassword("order_api_key"); + assertThat(orderApiKey).isEqualTo("Credential:order_api_key deleted successfully!"); + } + + @Test + public void whenRetrieveExistingCredential_thenReturnCredentialValue() { + String orderConfigJson = credentialService.getPassword("order_config_json"); + assertFalse(orderConfigJson.isEmpty()); + } + + @Test + public void whenCredentialPermissionCreated_thenAddToCredential() { + CredentialPermission orderConfig = credentialService.addCredentialPermission("order_config_json"); + List operations = orderConfig.getPermission() + .getOperations(); + String identity = orderConfig.getPermission() + .getActor() + .getIdentity(); + + CredentialPermission newOrderConfig = credentialService.getCredentialPermission("order_config_json"); + List newOperations = newOrderConfig.getPermission() + .getOperations(); + String newIdentity = newOrderConfig.getPermission() + .getActor() + .getIdentity(); + + assertThat(operations.size() == newOperations.size() && operations.containsAll(newOperations) && newOperations.containsAll(operations)); + assertThat(identity).isEqualTo(newIdentity); + } +}