diff --git a/clients/go/msd/client.go b/clients/go/msd/client.go index 3b9355bfdfc..779f1728bc7 100644 --- a/clients/go/msd/client.go +++ b/clients/go/msd/client.go @@ -436,10 +436,11 @@ func (client MSDClient) GetTransportPolicyRulesByDomain(domainName DomainName, m } } -func (client MSDClient) PutTransportPolicy(domainName DomainName, serviceName EntityName, auditRef string, payload *TransportPolicyRequest) (*TransportPolicyRules, error) { +func (client MSDClient) PutTransportPolicy(domainName DomainName, serviceName EntityName, auditRef string, resourceOwner string, payload *TransportPolicyRequest) (*TransportPolicyRules, error) { var data *TransportPolicyRules headers := map[string]string{ - "Y-Audit-Ref": auditRef, + "Athenz-Resource-Owner": resourceOwner, + "Y-Audit-Ref": auditRef, } url := client.URL + "/domain/" + fmt.Sprint(domainName) + "/service/" + fmt.Sprint(serviceName) + "/transportpolicy" contentBytes, err := json.Marshal(payload) @@ -515,9 +516,10 @@ func (client MSDClient) GetTransportPolicyRulesByService(domainName DomainName, } } -func (client MSDClient) DeleteTransportPolicy(domainName DomainName, serviceName EntityName, id int64, auditRef string) error { +func (client MSDClient) DeleteTransportPolicy(domainName DomainName, serviceName EntityName, id int64, auditRef string, resourceOwner string) error { headers := map[string]string{ - "Y-Audit-Ref": auditRef, + "Athenz-Resource-Owner": resourceOwner, + "Y-Audit-Ref": auditRef, } url := client.URL + "/domain/" + fmt.Sprint(domainName) + "/service/" + fmt.Sprint(serviceName) + "/transportpolicy/" + fmt.Sprint(id) resp, err := client.httpDelete(url, headers) @@ -621,13 +623,16 @@ func (client MSDClient) GetWorkloadsByIP(ip string, matchingTag string) (*Worklo } } -func (client MSDClient) PutDynamicWorkload(domainName DomainName, serviceName EntityName, options *WorkloadOptions) error { +func (client MSDClient) PutDynamicWorkload(domainName DomainName, serviceName EntityName, options *WorkloadOptions, resourceOwner string) error { + headers := map[string]string{ + "Athenz-Resource-Owner": resourceOwner, + } url := client.URL + "/domain/" + fmt.Sprint(domainName) + "/service/" + fmt.Sprint(serviceName) + "/workload/dynamic" contentBytes, err := json.Marshal(options) if err != nil { return err } - resp, err := client.httpPut(url, nil, contentBytes) + resp, err := client.httpPut(url, headers, contentBytes) if err != nil { return err } @@ -652,9 +657,12 @@ func (client MSDClient) PutDynamicWorkload(domainName DomainName, serviceName En } } -func (client MSDClient) DeleteDynamicWorkload(domainName DomainName, serviceName EntityName, instanceId PathElement) error { +func (client MSDClient) DeleteDynamicWorkload(domainName DomainName, serviceName EntityName, instanceId PathElement, resourceOwner string) error { + headers := map[string]string{ + "Athenz-Resource-Owner": resourceOwner, + } url := client.URL + "/domain/" + fmt.Sprint(domainName) + "/service/" + fmt.Sprint(serviceName) + "/instanceId/" + fmt.Sprint(instanceId) + "/workload/dynamic" - resp, err := client.httpDelete(url, nil) + resp, err := client.httpDelete(url, headers) if err != nil { return err } @@ -679,13 +687,16 @@ func (client MSDClient) DeleteDynamicWorkload(domainName DomainName, serviceName } } -func (client MSDClient) PutStaticWorkload(domainName DomainName, serviceName EntityName, staticWorkload *StaticWorkload) error { +func (client MSDClient) PutStaticWorkload(domainName DomainName, serviceName EntityName, staticWorkload *StaticWorkload, resourceOwner string) error { + headers := map[string]string{ + "Athenz-Resource-Owner": resourceOwner, + } url := client.URL + "/domain/" + fmt.Sprint(domainName) + "/service/" + fmt.Sprint(serviceName) + "/workload/static" contentBytes, err := json.Marshal(staticWorkload) if err != nil { return err } - resp, err := client.httpPut(url, nil, contentBytes) + resp, err := client.httpPut(url, headers, contentBytes) if err != nil { return err } @@ -710,9 +721,12 @@ func (client MSDClient) PutStaticWorkload(domainName DomainName, serviceName Ent } } -func (client MSDClient) DeleteStaticWorkload(domainName DomainName, serviceName EntityName, name StaticWorkloadName) error { +func (client MSDClient) DeleteStaticWorkload(domainName DomainName, serviceName EntityName, name StaticWorkloadName, resourceOwner string) error { + headers := map[string]string{ + "Athenz-Resource-Owner": resourceOwner, + } url := client.URL + "/domain/" + fmt.Sprint(domainName) + "/service/" + fmt.Sprint(serviceName) + "/name/" + fmt.Sprint(name) + "/workload/static" - resp, err := client.httpDelete(url, nil) + resp, err := client.httpDelete(url, headers) if err != nil { return err } @@ -851,13 +865,16 @@ func (client MSDClient) GetWorkloadsByDomainAndService(request *BulkWorkloadRequ } } -func (client MSDClient) PutCompositeInstance(domainName DomainName, serviceName EntityName, instance *CompositeInstance) error { +func (client MSDClient) PutCompositeInstance(domainName DomainName, serviceName EntityName, instance *CompositeInstance, resourceOwner string) error { + headers := map[string]string{ + "Athenz-Resource-Owner": resourceOwner, + } url := client.URL + "/domain/" + fmt.Sprint(domainName) + "/service/" + fmt.Sprint(serviceName) + "/workload/discover/instance" contentBytes, err := json.Marshal(instance) if err != nil { return err } - resp, err := client.httpPut(url, nil, contentBytes) + resp, err := client.httpPut(url, headers, contentBytes) if err != nil { return err } @@ -882,9 +899,12 @@ func (client MSDClient) PutCompositeInstance(domainName DomainName, serviceName } } -func (client MSDClient) DeleteCompositeInstance(domainName DomainName, serviceName EntityName, instance SimpleName) error { +func (client MSDClient) DeleteCompositeInstance(domainName DomainName, serviceName EntityName, instance SimpleName, resourceOwner string) error { + headers := map[string]string{ + "Athenz-Resource-Owner": resourceOwner, + } url := client.URL + "/domain/" + fmt.Sprint(domainName) + "/service/" + fmt.Sprint(serviceName) + "/workload/discover/instance/" + fmt.Sprint(instance) - resp, err := client.httpDelete(url, nil) + resp, err := client.httpDelete(url, headers) if err != nil { return err } diff --git a/clients/go/msd/msd_schema.go b/clients/go/msd/msd_schema.go index 705e3b147b5..84dac07bbfb 100644 --- a/clients/go/msd/msd_schema.go +++ b/clients/go/msd/msd_schema.go @@ -727,6 +727,7 @@ func init() { mPutTransportPolicy.Input("domainName", "DomainName", true, "", "", false, nil, "name of the domain") mPutTransportPolicy.Input("serviceName", "EntityName", true, "", "", false, nil, "Name of the service") mPutTransportPolicy.Input("auditRef", "String", false, "", "Y-Audit-Ref", false, nil, "Audit param required(not empty) if domain auditEnabled is true.") + mPutTransportPolicy.Input("resourceOwner", "String", false, "", "Athenz-Resource-Owner", false, nil, "Resource owner for the request") mPutTransportPolicy.Input("payload", "TransportPolicyRequest", false, "", "", false, nil, "Struct representing input transport policy") mPutTransportPolicy.Auth("msd.UpdateNetworkPolicy", "{domainName}:service.{serviceName}", false, "") mPutTransportPolicy.Expected("NO_CONTENT") @@ -759,6 +760,7 @@ func init() { mDeleteTransportPolicy.Input("serviceName", "EntityName", true, "", "", false, nil, "Name of the service") mDeleteTransportPolicy.Input("id", "Int64", true, "", "", false, nil, "Id of the assertion representing the transport policy") mDeleteTransportPolicy.Input("auditRef", "String", false, "", "Y-Audit-Ref", false, nil, "Audit param required(not empty) if domain auditEnabled is true.") + mDeleteTransportPolicy.Input("resourceOwner", "String", false, "", "Athenz-Resource-Owner", false, nil, "Resource owner for the request") mDeleteTransportPolicy.Auth("msd.DeleteNetworkPolicy", "{domainName}:service.{serviceName}", false, "") mDeleteTransportPolicy.Expected("NO_CONTENT") mDeleteTransportPolicy.Exception("BAD_REQUEST", "ResourceError", "") @@ -802,6 +804,7 @@ func init() { mPutDynamicWorkload.Input("domainName", "DomainName", true, "", "", false, nil, "name of the domain") mPutDynamicWorkload.Input("serviceName", "EntityName", true, "", "", false, nil, "name of the service") mPutDynamicWorkload.Input("options", "WorkloadOptions", false, "", "", false, nil, "metadata about the dynamic workload") + mPutDynamicWorkload.Input("resourceOwner", "String", false, "", "Athenz-Resource-Owner", false, nil, "Resource owner for the request") mPutDynamicWorkload.Auth("", "", true, "") mPutDynamicWorkload.Expected("NO_CONTENT") mPutDynamicWorkload.Exception("BAD_REQUEST", "ResourceError", "") @@ -817,6 +820,7 @@ func init() { mDeleteDynamicWorkload.Input("domainName", "DomainName", true, "", "", false, nil, "name of the domain") mDeleteDynamicWorkload.Input("serviceName", "EntityName", true, "", "", false, nil, "name of the service") mDeleteDynamicWorkload.Input("instanceId", "PathElement", true, "", "", false, nil, "unique instance id within provider's namespace") + mDeleteDynamicWorkload.Input("resourceOwner", "String", false, "", "Athenz-Resource-Owner", false, nil, "Resource owner for the request") mDeleteDynamicWorkload.Auth("update", "{domainName}:service.{serviceName}", false, "") mDeleteDynamicWorkload.Expected("NO_CONTENT") mDeleteDynamicWorkload.Exception("BAD_REQUEST", "ResourceError", "") @@ -832,6 +836,7 @@ func init() { mPutStaticWorkload.Input("domainName", "DomainName", true, "", "", false, nil, "name of the domain") mPutStaticWorkload.Input("serviceName", "EntityName", true, "", "", false, nil, "name of the service") mPutStaticWorkload.Input("staticWorkload", "StaticWorkload", false, "", "", false, nil, "Struct representing static workload entered by the user") + mPutStaticWorkload.Input("resourceOwner", "String", false, "", "Athenz-Resource-Owner", false, nil, "Resource owner for the request") mPutStaticWorkload.Auth("update", "{domainName}:service.{serviceName}", false, "") mPutStaticWorkload.Expected("NO_CONTENT") mPutStaticWorkload.Exception("BAD_REQUEST", "ResourceError", "") @@ -847,6 +852,7 @@ func init() { mDeleteStaticWorkload.Input("domainName", "DomainName", true, "", "", false, nil, "name of the domain") mDeleteStaticWorkload.Input("serviceName", "EntityName", true, "", "", false, nil, "name of the service") mDeleteStaticWorkload.Input("name", "StaticWorkloadName", true, "", "", false, nil, "name associated with the workload. In most cases will be a FQDN") + mDeleteStaticWorkload.Input("resourceOwner", "String", false, "", "Athenz-Resource-Owner", false, nil, "Resource owner for the request") mDeleteStaticWorkload.Auth("update", "{domainName}:service.{serviceName}", false, "") mDeleteStaticWorkload.Expected("NO_CONTENT") mDeleteStaticWorkload.Exception("BAD_REQUEST", "ResourceError", "") @@ -902,6 +908,7 @@ func init() { mPutCompositeInstance.Input("domainName", "DomainName", true, "", "", false, nil, "name of the domain") mPutCompositeInstance.Input("serviceName", "EntityName", true, "", "", false, nil, "name of the service") mPutCompositeInstance.Input("instance", "CompositeInstance", false, "", "", false, nil, "Generic instance") + mPutCompositeInstance.Input("resourceOwner", "String", false, "", "Athenz-Resource-Owner", false, nil, "Resource owner for the request") mPutCompositeInstance.Auth("update", "{domainName}:service.{serviceName}", false, "") mPutCompositeInstance.Expected("NO_CONTENT") mPutCompositeInstance.Exception("BAD_REQUEST", "ResourceError", "") @@ -917,6 +924,7 @@ func init() { mDeleteCompositeInstance.Input("domainName", "DomainName", true, "", "", false, nil, "name of the domain") mDeleteCompositeInstance.Input("serviceName", "EntityName", true, "", "", false, nil, "name of the service") mDeleteCompositeInstance.Input("instance", "SimpleName", true, "", "", false, nil, "instance name/id/key") + mDeleteCompositeInstance.Input("resourceOwner", "String", false, "", "Athenz-Resource-Owner", false, nil, "Resource owner for the request") mDeleteCompositeInstance.Auth("update", "{domainName}:service.{serviceName}", false, "") mDeleteCompositeInstance.Expected("NO_CONTENT") mDeleteCompositeInstance.Exception("BAD_REQUEST", "ResourceError", "") diff --git a/clients/java/msd/src/main/java/com/yahoo/athenz/msd/MSDClient.java b/clients/java/msd/src/main/java/com/yahoo/athenz/msd/MSDClient.java index 70d9aacd306..a8be6c3b6cf 100644 --- a/clients/java/msd/src/main/java/com/yahoo/athenz/msd/MSDClient.java +++ b/clients/java/msd/src/main/java/com/yahoo/athenz/msd/MSDClient.java @@ -210,9 +210,9 @@ public Workloads getWorkloadsByService(String domain, String service, String mat * @param options options for the new workload * @return WorkloadOptions */ - public WorkloadOptions putDynamicWorkload(String domain, String service, WorkloadOptions options) { + public WorkloadOptions putDynamicWorkload(String domain, String service, WorkloadOptions options, String resourceOwner) { try { - return client.putDynamicWorkload(domain, service, options); + return client.putDynamicWorkload(domain, service, options, resourceOwner); } catch (ClientResourceException ex) { throw new MSDClientException(ex.getCode(), ex.getData()); } catch (Exception ex) { @@ -227,9 +227,9 @@ public WorkloadOptions putDynamicWorkload(String domain, String service, Workloa * @param service name of the service * @param instanceId instanceId of the host */ - public void deleteDynamicWorkload(String domain, String service, String instanceId) { + public void deleteDynamicWorkload(String domain, String service, String instanceId, String resourceOwner) { try { - client.deleteDynamicWorkload(domain, service, instanceId); + client.deleteDynamicWorkload(domain, service, instanceId, resourceOwner); } catch (ClientResourceException ex) { throw new MSDClientException(ex.getCode(), ex.getData()); } catch (Exception ex) { @@ -245,9 +245,9 @@ public void deleteDynamicWorkload(String domain, String service, String instance * @param staticWorkload StaticWorkload object * @return WorkloadOptions */ - public StaticWorkload putStaticWorkload(String domain, String service, StaticWorkload staticWorkload) { + public StaticWorkload putStaticWorkload(String domain, String service, StaticWorkload staticWorkload, String resourceOwner) { try { - return client.putStaticWorkload(domain, service, staticWorkload); + return client.putStaticWorkload(domain, service, staticWorkload, resourceOwner); } catch (ClientResourceException ex) { throw new MSDClientException(ex.getCode(), ex.getData()); } catch (Exception ex) { @@ -262,9 +262,9 @@ public StaticWorkload putStaticWorkload(String domain, String service, StaticWor * @param service name of the service * @param name name of the static workload */ - public void deleteStaticWorkload(String domain, String service, String name) { + public void deleteStaticWorkload(String domain, String service, String name, String resourceOwner) { try { - client.deleteStaticWorkload(domain, service, name); + client.deleteStaticWorkload(domain, service, name, resourceOwner); } catch (ClientResourceException ex) { throw new MSDClientException(ex.getCode(), ex.getData()); } catch (Exception ex) { diff --git a/clients/java/msd/src/main/java/com/yahoo/athenz/msd/MSDRDLGeneratedClient.java b/clients/java/msd/src/main/java/com/yahoo/athenz/msd/MSDRDLGeneratedClient.java index 5db0a151bca..4c06fce6128 100644 --- a/clients/java/msd/src/main/java/com/yahoo/athenz/msd/MSDRDLGeneratedClient.java +++ b/clients/java/msd/src/main/java/com/yahoo/athenz/msd/MSDRDLGeneratedClient.java @@ -281,7 +281,7 @@ public TransportPolicyRules getTransportPolicyRulesByDomain(String domainName, S } } - public TransportPolicyRules putTransportPolicy(String domainName, String serviceName, String auditRef, TransportPolicyRequest payload) throws URISyntaxException, IOException { + public TransportPolicyRules putTransportPolicy(String domainName, String serviceName, String auditRef, String resourceOwner, TransportPolicyRequest payload) throws URISyntaxException, IOException { UriTemplateBuilder uriTemplateBuilder = new UriTemplateBuilder(baseUrl, "/domain/{domainName}/service/{serviceName}/transportpolicy") .resolveTemplate("domainName", domainName) .resolveTemplate("serviceName", serviceName); @@ -297,6 +297,9 @@ public TransportPolicyRules putTransportPolicy(String domainName, String service if (auditRef != null) { httpUriRequest.addHeader("Y-Audit-Ref", auditRef); } + if (resourceOwner != null) { + httpUriRequest.addHeader("Athenz-Resource-Owner", resourceOwner); + } HttpEntity httpResponseEntity = null; try (CloseableHttpResponse httpResponse = client.execute(httpUriRequest, httpContext)) { int code = httpResponse.getCode(); @@ -358,7 +361,7 @@ public TransportPolicyRules getTransportPolicyRulesByService(String domainName, } } - public TransportPolicyRules deleteTransportPolicy(String domainName, String serviceName, Long id, String auditRef) throws URISyntaxException, IOException { + public TransportPolicyRules deleteTransportPolicy(String domainName, String serviceName, Long id, String auditRef, String resourceOwner) throws URISyntaxException, IOException { UriTemplateBuilder uriTemplateBuilder = new UriTemplateBuilder(baseUrl, "/domain/{domainName}/service/{serviceName}/transportpolicy/{id}") .resolveTemplate("domainName", domainName) .resolveTemplate("serviceName", serviceName) @@ -373,6 +376,9 @@ public TransportPolicyRules deleteTransportPolicy(String domainName, String serv if (auditRef != null) { httpUriRequest.addHeader("Y-Audit-Ref", auditRef); } + if (resourceOwner != null) { + httpUriRequest.addHeader("Athenz-Resource-Owner", resourceOwner); + } HttpEntity httpResponseEntity = null; try (CloseableHttpResponse httpResponse = client.execute(httpUriRequest, httpContext)) { int code = httpResponse.getCode(); @@ -468,7 +474,7 @@ public Workloads getWorkloadsByIP(String ip, String matchingTag, java.util.Map 0 { service := string(serviceBytes[:idx]) - return service, nil + servicePostfix := serviceBytes[idx:] + return service, servicePostfix, nil } - return "", fmt.Errorf("unable to derive service name from metadata") + return "", nil, fmt.Errorf("unable to derive service name from metadata") } func GetProfile(metaEndpoint string) (string, error) { diff --git a/libs/java/auth_core/src/test/java/com/yahoo/athenz/auth/util/GlobStringsMatcherTest.java b/libs/java/auth_core/src/test/java/com/yahoo/athenz/auth/util/GlobStringsMatcherTest.java index 8eb697505d7..398846025bf 100644 --- a/libs/java/auth_core/src/test/java/com/yahoo/athenz/auth/util/GlobStringsMatcherTest.java +++ b/libs/java/auth_core/src/test/java/com/yahoo/athenz/auth/util/GlobStringsMatcherTest.java @@ -18,8 +18,8 @@ import org.testng.annotations.Test; -import static org.testng.AssertJUnit.assertFalse; -import static org.testng.AssertJUnit.assertTrue; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; public class GlobStringsMatcherTest { diff --git a/libs/java/dynamodb_client_factory/src/test/java/com/yahoo/athenz/db/dynamodb/DynamoDBClientFetcherImplTest.java b/libs/java/dynamodb_client_factory/src/test/java/com/yahoo/athenz/db/dynamodb/DynamoDBClientFetcherImplTest.java index c75db45ac38..9365b324e28 100644 --- a/libs/java/dynamodb_client_factory/src/test/java/com/yahoo/athenz/db/dynamodb/DynamoDBClientFetcherImplTest.java +++ b/libs/java/dynamodb_client_factory/src/test/java/com/yahoo/athenz/db/dynamodb/DynamoDBClientFetcherImplTest.java @@ -30,7 +30,7 @@ import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; -import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.Assert.assertNotNull; public class DynamoDBClientFetcherImplTest { diff --git a/libs/java/dynamodb_client_factory/src/test/java/com/yahoo/athenz/db/dynamodb/DynamoDBClientSettingsTest.java b/libs/java/dynamodb_client_factory/src/test/java/com/yahoo/athenz/db/dynamodb/DynamoDBClientSettingsTest.java index 5112d92f5e4..7c6f11e007b 100644 --- a/libs/java/dynamodb_client_factory/src/test/java/com/yahoo/athenz/db/dynamodb/DynamoDBClientSettingsTest.java +++ b/libs/java/dynamodb_client_factory/src/test/java/com/yahoo/athenz/db/dynamodb/DynamoDBClientSettingsTest.java @@ -24,7 +24,7 @@ import static org.mockito.Mockito.when; -import static org.testng.AssertJUnit.*; +import static org.testng.Assert.*; public class DynamoDBClientSettingsTest { @Test diff --git a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/cert/impl/DynamoDBCertRecordStoreTest.java b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/cert/impl/DynamoDBCertRecordStoreTest.java index f379ae4f13f..22874d10e47 100644 --- a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/cert/impl/DynamoDBCertRecordStoreTest.java +++ b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/cert/impl/DynamoDBCertRecordStoreTest.java @@ -35,8 +35,6 @@ import java.security.cert.X509Certificate; import static org.testng.Assert.*; -import static org.testng.AssertJUnit.assertFalse; -import static org.testng.AssertJUnit.assertTrue; public class DynamoDBCertRecordStoreTest { diff --git a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/cert/impl/DynamoDBSSHRecordStoreTest.java b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/cert/impl/DynamoDBSSHRecordStoreTest.java index a142d186389..678cbfd801f 100644 --- a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/cert/impl/DynamoDBSSHRecordStoreTest.java +++ b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/cert/impl/DynamoDBSSHRecordStoreTest.java @@ -29,8 +29,8 @@ import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import static org.testng.Assert.assertNotNull; -import static org.testng.AssertJUnit.assertFalse; -import static org.testng.AssertJUnit.assertTrue; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; public class DynamoDBSSHRecordStoreTest { diff --git a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/cert/impl/DynamoDBStatusCheckerFactoryTest.java b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/cert/impl/DynamoDBStatusCheckerFactoryTest.java index ef026c495f3..a20600afa39 100644 --- a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/cert/impl/DynamoDBStatusCheckerFactoryTest.java +++ b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/cert/impl/DynamoDBStatusCheckerFactoryTest.java @@ -18,10 +18,9 @@ import com.yahoo.athenz.common.server.status.StatusChecker; import com.yahoo.athenz.common.server.ServerResourceException; -import org.testng.AssertJUnit; import org.testng.annotations.Test; -import static org.testng.AssertJUnit.*; +import static org.testng.Assert.*; public class DynamoDBStatusCheckerFactoryTest { @@ -42,8 +41,8 @@ public void testTableNameNotSpecified() { dynamoDBStatusCheckerFactory.create(); fail(); } catch (ServerResourceException ex) { - AssertJUnit.assertEquals("DynamoDB table name not specified", ex.getMessage()); - AssertJUnit.assertEquals(503, ex.getCode()); + assertEquals("DynamoDB table name not specified", ex.getMessage()); + assertEquals(503, ex.getCode()); } } diff --git a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/cert/impl/DynamoDBStatusCheckerTest.java b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/cert/impl/DynamoDBStatusCheckerTest.java index 966de3b35ec..57b852c046c 100644 --- a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/cert/impl/DynamoDBStatusCheckerTest.java +++ b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/cert/impl/DynamoDBStatusCheckerTest.java @@ -37,7 +37,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; -import static org.testng.AssertJUnit.*; +import static org.testng.Assert.*; public class DynamoDBStatusCheckerTest { diff --git a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/notification/impl/AWSZTSHealthNotificationTaskTest.java b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/notification/impl/AWSZTSHealthNotificationTaskTest.java index 085b1563fe1..bbd6c20c6fa 100644 --- a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/notification/impl/AWSZTSHealthNotificationTaskTest.java +++ b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/notification/impl/AWSZTSHealthNotificationTaskTest.java @@ -38,8 +38,6 @@ import static io.athenz.server.aws.common.notification.impl.AWSZTSHealthNotificationTask.ZTS_PROP_NOTIFICATION_AWS_HEALTH_DOMAIN; import static org.mockito.Mockito.when; import static org.testng.Assert.*; -import static org.testng.Assert.assertTrue; -import static org.testng.AssertJUnit.assertEquals; public class AWSZTSHealthNotificationTaskTest { private ZTSClientNotification ztsClientNotification; diff --git a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/store/impl/AuthHistoryDynamoDBRecordTest.java b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/store/impl/AuthHistoryDynamoDBRecordTest.java index 40df5392c43..5e829a43aba 100644 --- a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/store/impl/AuthHistoryDynamoDBRecordTest.java +++ b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/store/impl/AuthHistoryDynamoDBRecordTest.java @@ -21,8 +21,8 @@ import org.testng.annotations.Test; import static org.testng.Assert.assertNotEquals; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; public class AuthHistoryDynamoDBRecordTest { diff --git a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/store/impl/DynamoDBAuthHistoryStoreConnectionTest.java b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/store/impl/DynamoDBAuthHistoryStoreConnectionTest.java index 1b994489e74..f88fb37b009 100644 --- a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/store/impl/DynamoDBAuthHistoryStoreConnectionTest.java +++ b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/store/impl/DynamoDBAuthHistoryStoreConnectionTest.java @@ -36,7 +36,7 @@ import java.util.function.Consumer; import java.util.stream.Stream; -import static org.testng.AssertJUnit.*; +import static org.testng.Assert.*; public class DynamoDBAuthHistoryStoreConnectionTest { diff --git a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/store/impl/DynamoDBAuthHistoryStoreFactoryTest.java b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/store/impl/DynamoDBAuthHistoryStoreFactoryTest.java index 26f2fa01ef7..2f3c1483b49 100644 --- a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/store/impl/DynamoDBAuthHistoryStoreFactoryTest.java +++ b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/store/impl/DynamoDBAuthHistoryStoreFactoryTest.java @@ -21,7 +21,7 @@ import com.yahoo.athenz.common.server.store.AuthHistoryStore; import org.testng.annotations.Test; -import static org.testng.AssertJUnit.*; +import static org.testng.Assert.*; public class DynamoDBAuthHistoryStoreFactoryTest { @Test diff --git a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/store/impl/DynamoDBAuthHistoryStoreTest.java b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/store/impl/DynamoDBAuthHistoryStoreTest.java index 62c8274ec4a..306b96820dc 100644 --- a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/store/impl/DynamoDBAuthHistoryStoreTest.java +++ b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/store/impl/DynamoDBAuthHistoryStoreTest.java @@ -24,7 +24,7 @@ import org.testng.annotations.Test; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.Assert.assertNotNull; public class DynamoDBAuthHistoryStoreTest { @Test diff --git a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/utils/DynamoDBUtilsTest.java b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/utils/DynamoDBUtilsTest.java index 295dbe356f8..ea0399042cf 100644 --- a/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/utils/DynamoDBUtilsTest.java +++ b/libs/java/server_aws_common/src/test/java/io/athenz/server/aws/common/utils/DynamoDBUtilsTest.java @@ -22,7 +22,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static org.testng.AssertJUnit.*; +import static org.testng.Assert.*; public class DynamoDBUtilsTest { diff --git a/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/notification/Notification.java b/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/notification/Notification.java index be4bb3d2688..d3dcdf09059 100644 --- a/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/notification/Notification.java +++ b/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/notification/Notification.java @@ -31,7 +31,9 @@ public enum Type { PENDING_ROLE_APPROVAL, PENDING_GROUP_APPROVAL, CERT_FAILED_REFRESH, - AWS_ZTS_HEALTH + AWS_ZTS_HEALTH, + ROLE_MEMBER_DECISION, + GROUP_MEMBER_DECISION } // type of the notification diff --git a/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/notification/NotificationServiceConstants.java b/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/notification/NotificationServiceConstants.java index 3988b408611..51ed2af050e 100644 --- a/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/notification/NotificationServiceConstants.java +++ b/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/notification/NotificationServiceConstants.java @@ -19,17 +19,21 @@ public final class NotificationServiceConstants { public static final String NOTIFICATION_PROP_SERVICE_FACTORY_CLASS = "athenz.zms.notification_service_factory_class"; - public static final String NOTIFICATION_DETAILS_DOMAIN = "domain"; - public static final String NOTIFICATION_DETAILS_ROLE = "role"; - public static final String NOTIFICATION_DETAILS_GROUP = "group"; - public static final String NOTIFICATION_DETAILS_MEMBER = "member"; - public static final String NOTIFICATION_DETAILS_REASON = "reason"; - public static final String NOTIFICATION_DETAILS_REQUESTER = "requester"; - public static final String NOTIFICATION_DETAILS_ROLES_LIST = "rolesList"; - public static final String NOTIFICATION_DETAILS_MEMBERS_LIST = "membersList"; - public static final String NOTIFICATION_DETAILS_UNREFRESHED_CERTS = "unrefreshedCerts"; - public static final String NOTIFICATION_DETAILS_AWS_ZTS_HEALTH = "awsZtsHealth"; - public static final String NOTIFICATION_DETAILS_AFFECTED_ZTS = "affectedZts"; + public static final String NOTIFICATION_DETAILS_DOMAIN = "domain"; + public static final String NOTIFICATION_DETAILS_ROLE = "role"; + public static final String NOTIFICATION_DETAILS_GROUP = "group"; + public static final String NOTIFICATION_DETAILS_MEMBER = "member"; + public static final String NOTIFICATION_DETAILS_REASON = "reason"; + public static final String NOTIFICATION_DETAILS_REQUESTER = "requester"; + public static final String NOTIFICATION_DETAILS_ROLES_LIST = "rolesList"; + public static final String NOTIFICATION_DETAILS_MEMBERS_LIST = "membersList"; + public static final String NOTIFICATION_DETAILS_UNREFRESHED_CERTS = "unrefreshedCerts"; + public static final String NOTIFICATION_DETAILS_AWS_ZTS_HEALTH = "awsZtsHealth"; + public static final String NOTIFICATION_DETAILS_AFFECTED_ZTS = "affectedZts"; + + public static final String NOTIFICATION_DETAILS_PENDING_MEMBERSHIP_DECISION_PRINCIPAL = "actionPrincipal"; + public static final String NOTIFICATION_DETAILS_PENDING_MEMBERSHIP_STATE = "pendingState"; + public static final String NOTIFICATION_DETAILS_PENDING_MEMBERSHIP_DECISION = "membershipDecision"; public static final String HTML_LOGO_CID_PLACEHOLDER = ""; public static final String CHARSET_UTF_8 = "UTF-8"; diff --git a/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/notification/impl/MetricNotificationService.java b/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/notification/impl/MetricNotificationService.java index 66245a4fccb..330bedcc6cb 100644 --- a/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/notification/impl/MetricNotificationService.java +++ b/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/notification/impl/MetricNotificationService.java @@ -42,6 +42,7 @@ public class MetricNotificationService implements NotificationService { public static final String METRIC_NOTIFICATION_GROUP_KEY = "group"; public static final String METRIC_NOTIFICATION_REASON_KEY = "reason"; public static final String METRIC_NOTIFICATION_REQUESTER_KEY = "requester"; + public static final String METRIC_NOTIFICATION_MEMBERSHIP_DECISION = "membership_decision"; private final Metric metric; diff --git a/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/store/ObjectStoreConnection.java b/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/store/ObjectStoreConnection.java index 9c20204ba86..e33e30cc7ac 100644 --- a/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/store/ObjectStoreConnection.java +++ b/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/store/ObjectStoreConnection.java @@ -101,6 +101,7 @@ public interface ObjectStoreConnection extends Closeable { boolean updateRoleMemberDisabledState(String domainName, String roleName, String member, String principal, int disabledState, String auditRef) throws ServerResourceException; boolean deletePendingRoleMember(String domainName, String roleName, String member, String principal, String auditRef) throws ServerResourceException; boolean confirmRoleMember(String domainName, String roleName, RoleMember roleMember, String principal, String auditRef) throws ServerResourceException; + RoleMember getPendingRoleMember(String domainName, String roleName, String memberName) throws ServerResourceException; DomainRoleMembers listDomainRoleMembers(String domainName) throws ServerResourceException; DomainRoleMember getPrincipalRoles(String principal, String domainName) throws ServerResourceException; @@ -131,6 +132,7 @@ public interface ObjectStoreConnection extends Closeable { DomainGroupMembers listDomainGroupMembers(String domainName) throws ServerResourceException; DomainGroupMember getPrincipalGroups(String principal, String domainName) throws ServerResourceException; List listGroupsWithUserAuthorityRestrictions() throws ServerResourceException; + GroupMember getPendingGroupMember(String domainName, String groupName, String memberName) throws ServerResourceException; // Policy commands diff --git a/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/store/impl/JDBCConnection.java b/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/store/impl/JDBCConnection.java index 37771713d2f..a4cecf3acbc 100644 --- a/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/store/impl/JDBCConnection.java +++ b/libs/java/server_common/src/main/java/com/yahoo/athenz/common/server/store/impl/JDBCConnection.java @@ -5484,6 +5484,46 @@ public String getPendingRoleMemberState(Integer roleId, String member) throws Se } + @Override + public RoleMember getPendingRoleMember(String domainName, String roleName, String memberName) throws ServerResourceException { + + final String caller = "getPendingRoleMember"; + int domainId = getDomainId(domainName); + if (domainId == 0) { + throw notFoundError(caller, JDBCConsts.OBJECT_DOMAIN, domainName); + } + int roleId = getRoleId(domainId, roleName); + if (roleId == 0) { + throw notFoundError(caller, JDBCConsts.OBJECT_ROLE, ResourceUtils.roleResourceName(domainName, roleName)); + } + + try (PreparedStatement ps = con.prepareStatement(SQL_GET_PENDING_ROLE_MEMBER)) { + ps.setInt(1, roleId); + ps.setString(2, memberName); + try (ResultSet rs = executeQuery(ps, caller)) { + if (rs.next()) { + RoleMember roleMember = new RoleMember(); + roleMember.setMemberName(memberName); + java.sql.Timestamp expiration = rs.getTimestamp(JDBCConsts.DB_COLUMN_EXPIRATION); + if (expiration != null) { + roleMember.setExpiration(Timestamp.fromMillis(expiration.getTime())); + } + java.sql.Timestamp reviewReminder = rs.getTimestamp(JDBCConsts.DB_COLUMN_REVIEW_REMINDER); + if (reviewReminder != null) { + roleMember.setReviewReminder(Timestamp.fromMillis(reviewReminder.getTime())); + } + roleMember.setRequestPrincipal(rs.getString(JDBCConsts.DB_COLUMN_REQ_PRINCIPAL)); + roleMember.setPendingState(rs.getString(JDBCConsts.DB_COLUMN_PENDING_STATE)); + return roleMember; + } else { + return null; + } + } + } catch (SQLException ex) { + throw sqlError(ex, caller); + } + } + public String getPendingGroupMemberState(Integer groupId, String member) throws ServerResourceException { final String caller = "getPendingGroupMemberState"; @@ -6986,6 +7026,37 @@ public List listGroupsWithUserAuthorityRestrictions() throws Ser return groups; } + @Override + public GroupMember getPendingGroupMember(String domainName, String groupName, String memberName) throws ServerResourceException { + + final String caller = "getPendingGroupMember"; + int domainId = getDomainId(domainName); + if (domainId == 0) { + throw notFoundError(caller, JDBCConsts.OBJECT_DOMAIN, domainName); + } + int groupId = getGroupId(domainId, groupName); + if (groupId == 0) { + throw notFoundError(caller, JDBCConsts.OBJECT_GROUP, ResourceUtils.groupResourceName(domainName, groupName)); + } + + try (PreparedStatement ps = con.prepareStatement(SQL_GET_PENDING_GROUP_MEMBER)) { + ps.setInt(1, groupId); + ps.setString(2, memberName); + try (ResultSet rs = executeQuery(ps, caller)) { + if (rs.next()) { + GroupMember groupMember = new GroupMember(); + groupMember.setMemberName(memberName); + groupMember.setRequestPrincipal(rs.getString(JDBCConsts.DB_COLUMN_REQ_PRINCIPAL)); + groupMember.setPendingState(rs.getString(JDBCConsts.DB_COLUMN_PENDING_STATE)); + return groupMember; + } else { + return null; + } + } + } catch (SQLException ex) { + throw sqlError(ex, caller); + } + } @Override public boolean updatePrincipal(String principal, int newState) throws ServerResourceException { diff --git a/libs/java/server_common/src/main/resources/messages/ServerCommon.properties b/libs/java/server_common/src/main/resources/messages/ServerCommon.properties index fbb1383bd70..0649ac11db9 100644 --- a/libs/java/server_common/src/main/resources/messages/ServerCommon.properties +++ b/libs/java/server_common/src/main/resources/messages/ServerCommon.properties @@ -21,3 +21,11 @@ athenz.notification.email.group_membership.reminder.subject=Group Membership App athenz.notification.email.domain.group_member.expiry.subject=Athenz Domain Group Member Expiration Notification athenz.notification.email.group_member.expiry.subject=Athenz Group Member Expiration Notification + +athenz.notification.email.pending_role_membership.decision.reject.subject=Athenz Pending Role Member Rejected + +athenz.notification.email.pending_role_membership.decision.approval.subject=Athenz Pending Role Member Approved + +athenz.notification.email.pending_group_membership.decision.reject.subject=Athenz Pending Group Member Rejected + +athenz.notification.email.pending_group_membership.decision.approval.subject=Athenz Pending Group Member Approved diff --git a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/cert/impl/JDBCCertRecordStoreStatusCheckerFactoryTest.java b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/cert/impl/JDBCCertRecordStoreStatusCheckerFactoryTest.java index f70b5e66e33..1ed1ff12eec 100644 --- a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/cert/impl/JDBCCertRecordStoreStatusCheckerFactoryTest.java +++ b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/cert/impl/JDBCCertRecordStoreStatusCheckerFactoryTest.java @@ -20,7 +20,7 @@ import com.yahoo.athenz.common.server.status.StatusChecker; import org.testng.annotations.Test; -import static org.testng.AssertJUnit.*; +import static org.testng.Assert.*; public class JDBCCertRecordStoreStatusCheckerFactoryTest { diff --git a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/cert/impl/JDBCCertRecordStoreStatusCheckerTest.java b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/cert/impl/JDBCCertRecordStoreStatusCheckerTest.java index 28aadcd5d10..1636e59e410 100644 --- a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/cert/impl/JDBCCertRecordStoreStatusCheckerTest.java +++ b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/cert/impl/JDBCCertRecordStoreStatusCheckerTest.java @@ -22,8 +22,8 @@ import org.mockito.Mockito; import org.testng.annotations.Test; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.fail; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; public class JDBCCertRecordStoreStatusCheckerTest { diff --git a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/cert/impl/JDBCCertRecordStoreTest.java b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/cert/impl/JDBCCertRecordStoreTest.java index 434ca64aa04..f8c541631b7 100644 --- a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/cert/impl/JDBCCertRecordStoreTest.java +++ b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/cert/impl/JDBCCertRecordStoreTest.java @@ -34,7 +34,6 @@ import com.yahoo.athenz.common.server.db.PoolableDataSource; import static org.testng.Assert.*; -import static org.testng.AssertJUnit.assertFalse; public class JDBCCertRecordStoreTest { diff --git a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/cert/impl/JDBCSSHRecordStoreTest.java b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/cert/impl/JDBCSSHRecordStoreTest.java index a0da69704a7..fe15bbfa968 100644 --- a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/cert/impl/JDBCSSHRecordStoreTest.java +++ b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/cert/impl/JDBCSSHRecordStoreTest.java @@ -28,7 +28,6 @@ import java.sql.SQLException; import static org.testng.Assert.*; -import static org.testng.AssertJUnit.assertFalse; public class JDBCSSHRecordStoreTest { diff --git a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/log/jetty/AthenzConnectionListenerTest.java b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/log/jetty/AthenzConnectionListenerTest.java index d137a0254dc..aa193ece7d4 100644 --- a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/log/jetty/AthenzConnectionListenerTest.java +++ b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/log/jetty/AthenzConnectionListenerTest.java @@ -4,8 +4,8 @@ import org.testng.annotations.Test; import static com.yahoo.athenz.common.server.log.jetty.AthenzConnectionListener.ATHENZ_PROP_CLEANUP_CLOSED_CONNECTION_INTERVAL; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNull; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; public class AthenzConnectionListenerTest { diff --git a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/log/jetty/ConnectionDataTest.java b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/log/jetty/ConnectionDataTest.java index 3aa4c3e6686..d96081e1d77 100644 --- a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/log/jetty/ConnectionDataTest.java +++ b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/log/jetty/ConnectionDataTest.java @@ -10,7 +10,7 @@ import static com.yahoo.athenz.common.server.log.jetty.JettyConnectionLogger.GENERAL_SSL_ERROR; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.testng.AssertJUnit.*; +import static org.testng.Assert.*; public class ConnectionDataTest { diff --git a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/log/jetty/JettyConnectionLoggerFactoryTest.java b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/log/jetty/JettyConnectionLoggerFactoryTest.java index 1eda0fdaaf9..adb3aa32e13 100644 --- a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/log/jetty/JettyConnectionLoggerFactoryTest.java +++ b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/log/jetty/JettyConnectionLoggerFactoryTest.java @@ -20,7 +20,7 @@ import org.testng.annotations.Test; -import static org.testng.AssertJUnit.*; +import static org.testng.Assert.*; public class JettyConnectionLoggerFactoryTest { @Test diff --git a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/notification/DomainRoleMembersFetcherCommonTest.java b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/notification/DomainRoleMembersFetcherCommonTest.java index 86c5143bf33..073862fd817 100644 --- a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/notification/DomainRoleMembersFetcherCommonTest.java +++ b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/notification/DomainRoleMembersFetcherCommonTest.java @@ -25,8 +25,8 @@ import java.util.*; import static com.yahoo.athenz.common.ServerCommonConsts.USER_DOMAIN_PREFIX; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertTrue; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; public class DomainRoleMembersFetcherCommonTest { diff --git a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/notification/NotificationMetricTest.java b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/notification/NotificationMetricTest.java index 30fb681ea9d..6a11426fe62 100644 --- a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/notification/NotificationMetricTest.java +++ b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/notification/NotificationMetricTest.java @@ -25,7 +25,6 @@ import static com.yahoo.athenz.common.server.notification.impl.MetricNotificationService.*; import static com.yahoo.athenz.common.server.notification.impl.MetricNotificationService.METRIC_NOTIFICATION_REVIEW_DAYS_KEY; import static org.testng.Assert.*; -import static org.testng.AssertJUnit.assertEquals; public class NotificationMetricTest { diff --git a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/notification/NotificationToMetricConverterCommonTest.java b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/notification/NotificationToMetricConverterCommonTest.java index f17fd0ab481..5e96c654bc9 100644 --- a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/notification/NotificationToMetricConverterCommonTest.java +++ b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/notification/NotificationToMetricConverterCommonTest.java @@ -19,8 +19,7 @@ import com.yahoo.rdl.Timestamp; import org.testng.annotations.Test; - -import static org.testng.AssertJUnit.assertEquals; +import static org.testng.Assert.assertEquals; public class NotificationToMetricConverterCommonTest { diff --git a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/notification/impl/MetricNotificationServiceTest.java b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/notification/impl/MetricNotificationServiceTest.java index 3636a43dd08..5aa6eafe416 100644 --- a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/notification/impl/MetricNotificationServiceTest.java +++ b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/notification/impl/MetricNotificationServiceTest.java @@ -26,8 +26,8 @@ import java.util.*; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertTrue; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; public class MetricNotificationServiceTest { diff --git a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/store/impl/JDBCConnectionTest.java b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/store/impl/JDBCConnectionTest.java index 00d018f6fd0..1e3b81e1685 100644 --- a/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/store/impl/JDBCConnectionTest.java +++ b/libs/java/server_common/src/test/java/com/yahoo/athenz/common/server/store/impl/JDBCConnectionTest.java @@ -16162,4 +16162,176 @@ boolean verifyDomainRoleMemberTimestamp(List members, return false; } + + @Test + public void testGetPendingRoleMember() throws Exception { + JDBCConnection jdbcConn = new JDBCConnection(mockConn, false); + Mockito.when(mockResultSet.next()).thenReturn(true).thenReturn(true).thenReturn(true); + Mockito.when(mockResultSet.getInt(1)).thenReturn(321).thenReturn(456); + Mockito.when(mockResultSet.getTimestamp(JDBCConsts.DB_COLUMN_EXPIRATION)) + .thenReturn(new java.sql.Timestamp(1454358916)); + Mockito.when(mockResultSet.getTimestamp(JDBCConsts.DB_COLUMN_REVIEW_REMINDER)) + .thenReturn(new java.sql.Timestamp(1459958916)); + Mockito.when(mockResultSet.getString(JDBCConsts.DB_COLUMN_REQ_PRINCIPAL)).thenReturn("user.bob"); + Mockito.when(mockResultSet.getString(JDBCConsts.DB_COLUMN_PENDING_STATE)) + .thenReturn(JDBCConsts.PENDING_REQUEST_ADD_STATE); + + RoleMember roleMember = jdbcConn.getPendingRoleMember("testDomain", "role1", "user.joe"); + assertEquals(roleMember.getPendingState(), JDBCConsts.PENDING_REQUEST_ADD_STATE); + assertEquals(roleMember.getMemberName(), "user.joe"); + assertEquals(roleMember.getRequestPrincipal(), "user.bob"); + assertEquals(roleMember.getExpiration(), Timestamp.fromMillis(1454358916)); + assertEquals(roleMember.getReviewReminder(), Timestamp.fromMillis(1459958916)); + jdbcConn.close(); + } + + @Test + public void testGetPendingRoleMemberNullReview() throws Exception { + JDBCConnection jdbcConn = new JDBCConnection(mockConn, false); + Mockito.when(mockResultSet.next()).thenReturn(true).thenReturn(true).thenReturn(true); + Mockito.when(mockResultSet.getInt(1)).thenReturn(321).thenReturn(456); + Mockito.when(mockResultSet.getTimestamp(JDBCConsts.DB_COLUMN_EXPIRATION)) + .thenReturn(new java.sql.Timestamp(1454358916)); + Mockito.when(mockResultSet.getString(JDBCConsts.DB_COLUMN_REQ_PRINCIPAL)).thenReturn("user.bob"); + Mockito.when(mockResultSet.getString(JDBCConsts.DB_COLUMN_PENDING_STATE)) + .thenReturn(JDBCConsts.PENDING_REQUEST_DELETE_STATE); + + RoleMember roleMember = jdbcConn.getPendingRoleMember("testDomain", "role1", "user.joe"); + assertEquals(roleMember.getPendingState(), JDBCConsts.PENDING_REQUEST_DELETE_STATE); + assertEquals(roleMember.getMemberName(), "user.joe"); + assertEquals(roleMember.getRequestPrincipal(), "user.bob"); + assertEquals(roleMember.getExpiration(), Timestamp.fromMillis(1454358916)); + assertNull(roleMember.getReviewReminder()); + jdbcConn.close(); + } + + @Test + public void testGetPendingRoleMemberNotFound() throws Exception { + JDBCConnection jdbcConn = new JDBCConnection(mockConn, false); + Mockito.when(mockResultSet.next()).thenReturn(true).thenReturn(true).thenReturn(false); + Mockito.when(mockResultSet.getInt(1)).thenReturn(321).thenReturn(456); + + assertNull(jdbcConn.getPendingRoleMember("testDomain", "role1", "user.joe")); + jdbcConn.close(); + } + + @Test + public void testGetPendingRoleMemberException() throws Exception { + JDBCConnection jdbcConn = new JDBCConnection(mockConn, false); + Mockito.when(mockResultSet.next()).thenReturn(true).thenReturn(true).thenReturn(false); + Mockito.when(mockResultSet.getInt(1)).thenReturn(321).thenReturn(456); + Mockito.when(mockPrepStmt.executeQuery()).thenReturn(mockResultSet) + .thenReturn(mockResultSet).thenThrow(new SQLException("failed operation", "state", 1001)); + + try { + jdbcConn.getPendingRoleMember("testDomain", "role1", "user.joe"); + fail(); + } catch (ServerResourceException ex) { + assertEquals(ex.getCode(), ServerResourceException.INTERNAL_SERVER_ERROR); + } + jdbcConn.close(); + } + + @Test + public void testGetPendingRoleMemberDomainNotFound() throws Exception { + JDBCConnection jdbcConn = new JDBCConnection(mockConn, false); + Mockito.when(mockResultSet.next()).thenReturn(true); + Mockito.when(mockResultSet.getInt(1)).thenReturn(0); + + try { + jdbcConn.getPendingRoleMember("testDomain", "role1", "user.joe"); + fail(); + } catch (ServerResourceException ex) { + assertEquals(ex.getCode(), ServerResourceException.NOT_FOUND); + } + jdbcConn.close(); + } + + @Test + public void testGetPendingRoleMemberRoleNotFound() throws Exception { + JDBCConnection jdbcConn = new JDBCConnection(mockConn, false); + Mockito.when(mockResultSet.next()).thenReturn(true).thenReturn(true); + Mockito.when(mockResultSet.getInt(1)).thenReturn(123).thenReturn(0); + + try { + jdbcConn.getPendingRoleMember("testDomain", "role1", "user.joe"); + fail(); + } catch (ServerResourceException ex) { + assertEquals(ex.getCode(), ServerResourceException.NOT_FOUND); + } + jdbcConn.close(); + } + + @Test + public void testGetPendingGroupMember() throws Exception { + JDBCConnection jdbcConn = new JDBCConnection(mockConn, false); + Mockito.when(mockResultSet.next()).thenReturn(true).thenReturn(true).thenReturn(true); + Mockito.when(mockResultSet.getInt(1)).thenReturn(321).thenReturn(456); + Mockito.when(mockResultSet.getString(JDBCConsts.DB_COLUMN_REQ_PRINCIPAL)).thenReturn("user.bob"); + Mockito.when(mockResultSet.getString(JDBCConsts.DB_COLUMN_PENDING_STATE)) + .thenReturn(JDBCConsts.PENDING_REQUEST_ADD_STATE); + + GroupMember groupMember = jdbcConn.getPendingGroupMember("testDomain", "group1", "user.joe"); + assertEquals(groupMember.getPendingState(), JDBCConsts.PENDING_REQUEST_ADD_STATE); + assertEquals(groupMember.getMemberName(), "user.joe"); + assertEquals(groupMember.getRequestPrincipal(), "user.bob"); + jdbcConn.close(); + } + + @Test + public void testGetPendingGroupMemberNotFound() throws Exception { + JDBCConnection jdbcConn = new JDBCConnection(mockConn, false); + Mockito.when(mockResultSet.next()).thenReturn(true).thenReturn(true).thenReturn(false); + Mockito.when(mockResultSet.getInt(1)).thenReturn(321).thenReturn(456); + + assertNull(jdbcConn.getPendingGroupMember("testDomain", "group1", "user.joe")); + jdbcConn.close(); + } + + @Test + public void testGetPendingGroupMemberException() throws Exception { + JDBCConnection jdbcConn = new JDBCConnection(mockConn, false); + Mockito.when(mockResultSet.next()).thenReturn(true).thenReturn(true).thenReturn(false); + Mockito.when(mockResultSet.getInt(1)).thenReturn(321).thenReturn(456); + Mockito.when(mockPrepStmt.executeQuery()).thenReturn(mockResultSet) + .thenReturn(mockResultSet).thenThrow(new SQLException("failed operation", "state", 1001)); + + try { + jdbcConn.getPendingGroupMember("testDomain", "group1", "user.joe"); + fail(); + } catch (ServerResourceException ex) { + assertEquals(ex.getCode(), ServerResourceException.INTERNAL_SERVER_ERROR); + } + jdbcConn.close(); + } + + @Test + public void testGetPendingGroupMemberDomainNotFound() throws Exception { + JDBCConnection jdbcConn = new JDBCConnection(mockConn, false); + Mockito.when(mockResultSet.next()).thenReturn(true); + Mockito.when(mockResultSet.getInt(1)).thenReturn(0); + + try { + jdbcConn.getPendingGroupMember("testDomain", "group1", "user.joe"); + fail(); + } catch (ServerResourceException ex) { + assertEquals(ex.getCode(), ServerResourceException.NOT_FOUND); + } + jdbcConn.close(); + } + + @Test + public void testGetPendingGroupMemberGroupNotFound() throws Exception { + JDBCConnection jdbcConn = new JDBCConnection(mockConn, false); + Mockito.when(mockResultSet.next()).thenReturn(true).thenReturn(true); + Mockito.when(mockResultSet.getInt(1)).thenReturn(123).thenReturn(0); + + try { + jdbcConn.getPendingGroupMember("testDomain", "group1", "user.joe"); + fail(); + } catch (ServerResourceException ex) { + assertEquals(ex.getCode(), ServerResourceException.NOT_FOUND); + } + jdbcConn.close(); + } } diff --git a/libs/java/server_common/src/test/resources/messages/ServerCommon.properties b/libs/java/server_common/src/test/resources/messages/ServerCommon.properties index b4edd0c5891..5564556c756 100644 --- a/libs/java/server_common/src/test/resources/messages/ServerCommon.properties +++ b/libs/java/server_common/src/test/resources/messages/ServerCommon.properties @@ -1 +1,5 @@ athenz.notification.email.role_member.expiry.subject=Athenz Role Member Expiration Notification +athenz.notification.email.pending_role_membership.decision.reject.subject=Athenz Pending Role Member Rejected +athenz.notification.email.pending_role_membership.decision.approval.subject=Athenz Pending Role Member Approved +athenz.notification.email.pending_group_membership.decision.reject.subject=Athenz Pending Group Member Rejected +athenz.notification.email.pending_group_membership.decision.approval.subject=Athenz Pending Group Member Approved diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/DBService.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/DBService.java index 92de552e396..5457acaaa3a 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/DBService.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/DBService.java @@ -3561,7 +3561,11 @@ DomainList lookupDomainByRole(String roleMember, String roleName) { } List listRoles(String domainName) { - try (ObjectStoreConnection con = store.getConnection(true, false)) { + return listRoles(domainName, false); + } + + List listRoles(String domainName, boolean readWrite) { + try (ObjectStoreConnection con = store.getConnection(true, readWrite)) { return con.listRoles(domainName); } catch (ServerResourceException ex) { throw ZMSUtils.error(ex); @@ -9333,8 +9337,12 @@ private boolean processDeleteDomainDependency(ObjectStoreConnection con, String } public ServiceIdentityList listServiceDependencies(String domainName) { + return listServiceDependencies(domainName, false); + } - try (ObjectStoreConnection con = store.getConnection(true, false)) { + public ServiceIdentityList listServiceDependencies(String domainName, boolean readWrite) { + + try (ObjectStoreConnection con = store.getConnection(true, readWrite)) { ServiceIdentityList serviceIdentityList = new ServiceIdentityList(); serviceIdentityList.setNames(con.listServiceDependencies(domainName)); return serviceIdentityList; @@ -9923,6 +9931,36 @@ public void executePutPrincipalState(ResourceContext ctx, final String domainNam } } + RoleMember getPendingRoleMember(String domainName, String roleName, String memberName) { + final String caller = "getPendingRoleMember"; + try (ObjectStoreConnection con = store.getConnection(true, false)) { + RoleMember pendingMember = con.getPendingRoleMember(domainName, roleName, memberName); + if (pendingMember == null) { + throw ZMSUtils.notFoundError("Pending role member " + memberName + " not found", caller); + } + return pendingMember; + } catch (ServerResourceException ex) { + LOG.error("getPendingRoleMember: error getting pending member {} from {}:role.{} - error {}", + memberName, domainName, roleName, ex.getMessage()); + throw ZMSUtils.error(ex); + } + } + + GroupMember getPendingGroupMember(String domainName, String groupName, String memberName) { + final String caller = "getPendingGroupMember"; + try (ObjectStoreConnection con = store.getConnection(true, false)) { + GroupMember pendingMember = con.getPendingGroupMember(domainName, groupName, memberName); + if (pendingMember == null) { + throw ZMSUtils.notFoundError("Pending group member " + memberName + " not found", caller); + } + return pendingMember; + } catch (ServerResourceException ex) { + LOG.error("getPendingGroupMember: error getting pending group member {} from {}:group.{} - error {}", + memberName, domainName, groupName, ex.getMessage()); + throw ZMSUtils.error(ex); + } + } + class UserAuthorityFilterEnforcer implements Runnable { public UserAuthorityFilterEnforcer() { diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSConsts.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSConsts.java index 496d8dbd0b7..d042518b1fe 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSConsts.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSConsts.java @@ -280,6 +280,8 @@ public final class ZMSConsts { //pending member public static final String PENDING_REQUEST_ADD_STATE = "ADD"; public static final String PENDING_REQUEST_DELETE_STATE = "DELETE"; + public static final String PENDING_REQUEST_APPROVE = "approve"; + public static final String PENDING_REQUEST_REJECT = "reject"; public static final String ZMS_PROP_JSON_MAX_NESTING_DEPTH = "athenz.zms.json_max_nesting_depth"; public static final String ZMS_PROP_JSON_MAX_NUMBER_LENGTH = "athenz.zms.json_max_number_length"; diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSImpl.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSImpl.java index 02f465ddc9b..48c280a46f1 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSImpl.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSImpl.java @@ -52,9 +52,7 @@ import com.yahoo.athenz.common.server.util.config.providers.ConfigProviderFile; import com.yahoo.athenz.common.utils.SignUtils; import com.yahoo.athenz.zms.config.*; -import com.yahoo.athenz.zms.notification.PutGroupMembershipNotificationTask; -import com.yahoo.athenz.zms.notification.PutRoleMembershipNotificationTask; -import com.yahoo.athenz.zms.notification.ZMSNotificationTaskFactory; +import com.yahoo.athenz.zms.notification.*; import com.yahoo.athenz.zms.provider.DomainDependencyProviderResponse; import com.yahoo.athenz.zms.provider.ServiceProviderClient; import com.yahoo.athenz.zms.provider.ServiceProviderManager; @@ -5035,23 +5033,76 @@ void sendMembershipApprovalNotification(final String domain, final String org, f } void sendGroupMembershipApprovalNotification(final String domain, final String org, final String groupName, - final String member, final String auditRef, final String principal, - final Group group) { + final String member, final String auditRef, final String principal, final Group group) { + + if (LOG.isDebugEnabled()) { + LOG.debug("Sending Group Membership Approval notification after putGroupMembership"); + } + Map details = new HashMap<>(); details.put(NOTIFICATION_DETAILS_DOMAIN, domain); details.put(NOTIFICATION_DETAILS_GROUP, groupName); details.put(NOTIFICATION_DETAILS_MEMBER, member); details.put(NOTIFICATION_DETAILS_REASON, auditRef); details.put(NOTIFICATION_DETAILS_REQUESTER, principal); - if (LOG.isDebugEnabled()) { - LOG.debug("Sending Group Membership Approval notification after putGroupMembership"); - } List notifications = new PutGroupMembershipNotificationTask(domain, org, group, details, dbService, userDomainPrefix, notificationToEmailConverterCommon).getNotifications(); notificationManager.sendNotifications(notifications); } + void sendRoleMembershipDecisionNotification(final String domain, final String roleName, + final RoleMember roleMember, final String auditRef, final String actionPrincipal, + final RoleMember pendingRoleMember) { + + if (LOG.isDebugEnabled()) { + LOG.debug("Sending role membership decision notification after putMembershipDecision"); + } + + Map details = new HashMap<>(); + details.put(NOTIFICATION_DETAILS_DOMAIN, domain); + details.put(NOTIFICATION_DETAILS_ROLE, roleName); + details.put(NOTIFICATION_DETAILS_MEMBER, roleMember.getMemberName()); + details.put(NOTIFICATION_DETAILS_REASON, auditRef); + details.put(NOTIFICATION_DETAILS_REQUESTER, pendingRoleMember.getRequestPrincipal()); + details.put(NOTIFICATION_DETAILS_PENDING_MEMBERSHIP_DECISION_PRINCIPAL, actionPrincipal); + details.put(NOTIFICATION_DETAILS_PENDING_MEMBERSHIP_STATE, pendingRoleMember.getPendingState()); + String membershipDecision = roleMember.getApproved() + ? ZMSConsts.PENDING_REQUEST_APPROVE : ZMSConsts.PENDING_REQUEST_REJECT; + details.put(NOTIFICATION_DETAILS_PENDING_MEMBERSHIP_DECISION, membershipDecision); + + List notifications = new PutRoleMembershipDecisionNotificationTask(details, + roleMember.getApproved(), dbService, userDomainPrefix, + notificationToEmailConverterCommon).getNotifications(); + notificationManager.sendNotifications(notifications); + } + + void sendGroupMembershipDecisionNotification(final String domain, final String groupName, + final GroupMember groupMember, final String auditRef, final String actionPrincipal, + final GroupMember pendingGroupMember) { + + if (LOG.isDebugEnabled()) { + LOG.debug("Sending group membership decision notification after putGroupMembershipDecision"); + } + + Map details = new HashMap<>(); + details.put(NOTIFICATION_DETAILS_DOMAIN, domain); + details.put(NOTIFICATION_DETAILS_GROUP, groupName); + details.put(NOTIFICATION_DETAILS_MEMBER, groupMember.getMemberName()); + details.put(NOTIFICATION_DETAILS_REASON, auditRef); + details.put(NOTIFICATION_DETAILS_REQUESTER, pendingGroupMember.getRequestPrincipal()); + details.put(NOTIFICATION_DETAILS_PENDING_MEMBERSHIP_DECISION_PRINCIPAL, actionPrincipal); + details.put(NOTIFICATION_DETAILS_PENDING_MEMBERSHIP_STATE, pendingGroupMember.getPendingState()); + String membershipDecision = groupMember.getApproved() ? + ZMSConsts.PENDING_REQUEST_APPROVE : ZMSConsts.PENDING_REQUEST_REJECT; + details.put(NOTIFICATION_DETAILS_PENDING_MEMBERSHIP_DECISION, membershipDecision); + + List notifications = new PutGroupMembershipDecisionNotificationTask(details, + groupMember.getApproved(), dbService, userDomainPrefix, + notificationToEmailConverterCommon).getNotifications(); + notificationManager.sendNotifications(notifications); + } + @Override public void deletePendingMembership(ResourceContext ctx, String domainName, String roleName, String memberName, String auditRef) { @@ -7008,6 +7059,7 @@ void setDomainDataAttributes(DomainData domainData, Domain domain) { domainData.setCertDnsDomain(domain.getCertDnsDomain()); domainData.setMemberPurgeExpiryDays(domain.getMemberPurgeExpiryDays()); domainData.setContacts(domain.getContacts()); + domainData.setResourceOwnership(domain.getResourceOwnership()); } SignedDomain retrieveSignedDomain(Domain domain, final String metaAttr, boolean setMetaDataOnly, boolean masterCopy, boolean includeConditions) { @@ -7810,13 +7862,15 @@ private void tenancyRegisterDomainDependency(ResourceContext ctx, String tenantD } private void tenancyDeregisterDomainDependency(ResourceContext ctx, String tenantDomain, String provSvcDomain, - String provSvcName, String auditRef, String caller) { + String provSvcName, String auditRef, String caller) { final String serviceToDeregister = provSvcDomain + "." + provSvcName; if (serviceProviderManager.isServiceProvider(serviceToDeregister)) { - boolean tenantDomainRolesExist = isTenantDomainRolesExist(tenantDomain, provSvcDomain, provSvcName); - DomainList domainList = dbService.listDomainDependencies(serviceToDeregister); - if (!tenantDomainRolesExist && domainList.getNames().contains(tenantDomain)) { - dbService.deleteDomainDependency(ctx, tenantDomain, serviceToDeregister, auditRef, caller); + ServiceIdentityList serviceIdentityList = dbService.listServiceDependencies(tenantDomain, true); + if (serviceIdentityList.getNames().contains(serviceToDeregister)) { + boolean tenantDomainRolesExist = isTenantDomainRolesExist(tenantDomain, provSvcDomain, provSvcName); + if (!tenantDomainRolesExist) { + dbService.deleteDomainDependency(ctx, tenantDomain, serviceToDeregister, auditRef, caller); + } } } } @@ -8422,15 +8476,15 @@ public void deleteProviderResourceGroupRoles(ResourceContext ctx, String tenantD } } - private boolean isTenantDomainRolesExist(String tenantDomain, String provSvcDomain, String provSvcName) { - final String provider = provSvcDomain + "." + provSvcName; - List dependentResourceGroups = getDependentServiceResourceGroupList(tenantDomain).getServiceAndResourceGroups().stream() - .filter(dependency -> dependency.getDomain().equals(tenantDomain) && dependency.getService().equals(provider)) - .findAny() - .map(DependentServiceResourceGroup::getResourceGroups) - .orElse(new ArrayList<>()); - - return !dependentResourceGroups.isEmpty(); + private boolean isTenantDomainRolesExist(final String tenantDomain, final String provSvcDomain, final String provSvcName) { + final String rolePrefix = ZMSUtils.getTenantResourceGroupRolePrefix(provSvcName, tenantDomain, ""); + final List tenantDomainRoles = dbService.listRoles(provSvcDomain, true); + for (String tenantDomainRole : tenantDomainRoles) { + if (tenantDomainRole.startsWith(rolePrefix)) { + return true; + } + } + return false; } public ProviderResourceGroupRoles getProviderResourceGroupRoles(ResourceContext ctx, String tenantDomain, @@ -10002,7 +10056,14 @@ public void putMembershipDecision(ResourceContext ctx, String domainName, String role.getAuditEnabled(), disallowGroups, caller); } + //get the pending member details to send notification + + RoleMember pendingMember = dbService.getPendingRoleMember(domainName, roleName, roleMember.getMemberName()); + dbService.executePutMembershipDecision(ctx, domainName, roleName, roleMember, auditRef, caller); + + sendRoleMembershipDecisionNotification(domainName, roleName, roleMember, auditRef, + principal.getFullName(), pendingMember); } private void validatePutMembershipDecisionAuthorization(final Principal principal, final AthenzDomain domain, @@ -11549,7 +11610,14 @@ public void putGroupMembershipDecision(ResourceContext ctx, String domainName, S userAuthorityFilterSet, principalDomainFilter, caller); } + //get the pending group member details to send notification + + GroupMember pendingMember = dbService.getPendingGroupMember(domainName, groupName, memberName); + dbService.executePutGroupMembershipDecision(ctx, domainName, group, groupMember, auditRef); + + sendGroupMembershipDecisionNotification(domainName, groupName, + groupMember, auditRef, principal.getFullName(), pendingMember); } @Override diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/notification/MembershipDecisionNotificationCommon.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/notification/MembershipDecisionNotificationCommon.java new file mode 100644 index 00000000000..40682ea616e --- /dev/null +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/notification/MembershipDecisionNotificationCommon.java @@ -0,0 +1,80 @@ +/* + * Copyright The Athenz Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.yahoo.athenz.zms.notification; + +import com.yahoo.athenz.auth.AuthorityConsts; +import com.yahoo.athenz.auth.util.AthenzUtils; +import com.yahoo.athenz.common.server.notification.DomainRoleMembersFetcher; +import com.yahoo.athenz.zms.DBService; +import com.yahoo.athenz.zms.Group; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jetty.util.StringUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static com.yahoo.athenz.common.ServerCommonConsts.ADMIN_ROLE_NAME; + +public class MembershipDecisionNotificationCommon { + private static final Logger LOGGER = LoggerFactory.getLogger(MembershipDecisionNotificationCommon.class); + private final DBService dbService; + private final DomainRoleMembersFetcher domainRoleMembersFetcher; + private final String userDomainPrefix; + + MembershipDecisionNotificationCommon(DBService dbService, DomainRoleMembersFetcher domainRoleMembersFetcher, String userDomainPrefix) { + this.dbService = dbService; + this.domainRoleMembersFetcher = domainRoleMembersFetcher; + this.userDomainPrefix = userDomainPrefix; + } + + public Set getRecipients(List members) { + Set notifyMembers = new HashSet<>(); + for (String memberName : members) { + if (StringUtils.isEmpty(memberName)) { + continue; + } + int idx = memberName.indexOf(AuthorityConsts.GROUP_SEP); + if (idx != -1) { + final String domainName = memberName.substring(0, idx); + final String groupName = memberName.substring(idx + AuthorityConsts.GROUP_SEP.length()); + Group group = dbService.getGroup(domainName, groupName, Boolean.FALSE, Boolean.FALSE); + if (group == null) { + LOGGER.error("unable to retrieve group: {} in domain: {}", groupName, domainName); + continue; + } + if (!StringUtil.isEmpty(group.getNotifyRoles())) { + notifyMembers.addAll(NotificationUtils.extractNotifyRoleMembers(domainRoleMembersFetcher, + domainName, group.getNotifyRoles())); + } else { + notifyMembers.addAll(domainRoleMembersFetcher.getDomainRoleMembers(domainName, ADMIN_ROLE_NAME)); + } + } else { + final String domainName = AthenzUtils.extractPrincipalDomainName(memberName); + if (userDomainPrefix.equals(domainName + ".")) { + notifyMembers.add(memberName); + } else { + // domain role fetcher only returns the human users + notifyMembers.addAll(domainRoleMembersFetcher.getDomainRoleMembers(domainName, ADMIN_ROLE_NAME)); + } + } + } + return notifyMembers; + } +} diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/notification/PutGroupMembershipDecisionNotificationTask.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/notification/PutGroupMembershipDecisionNotificationTask.java new file mode 100644 index 00000000000..a14d4bdf28f --- /dev/null +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/notification/PutGroupMembershipDecisionNotificationTask.java @@ -0,0 +1,162 @@ +/* + * Copyright The Athenz Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.yahoo.athenz.zms.notification; + +import com.yahoo.athenz.common.server.notification.*; +import com.yahoo.athenz.zms.DBService; +import com.yahoo.rdl.Timestamp; + +import java.text.MessageFormat; +import java.util.*; + +import static com.yahoo.athenz.common.server.notification.NotificationServiceConstants.*; +import static com.yahoo.athenz.common.server.notification.impl.MetricNotificationService.*; + +public class PutGroupMembershipDecisionNotificationTask implements NotificationTask { + + private final Map details; + private final NotificationCommon notificationCommon; + private final static String DESCRIPTION = "Pending Group Membership Decision Notification"; + private final PutGroupMembershipDecisionNotificationToEmailConverter putMembershipNotificationToEmailConverter; + private final PutGroupMembershipDecisionNotificationToMetricConverter putMembershipNotificationToMetricConverter; + private final DBService dbService; + private final DomainRoleMembersFetcher domainRoleMembersFetcher; + private final String userDomainPrefix; + + public PutGroupMembershipDecisionNotificationTask(Map details, Boolean approved, DBService dbService, + String userDomainPrefix, NotificationToEmailConverterCommon notificationToEmailConverterCommon) { + this.details = details; + this.userDomainPrefix = userDomainPrefix; + this.domainRoleMembersFetcher = new DomainRoleMembersFetcher(dbService, userDomainPrefix); + this.notificationCommon = new NotificationCommon(domainRoleMembersFetcher, userDomainPrefix); + this.putMembershipNotificationToEmailConverter = + new PutGroupMembershipDecisionNotificationToEmailConverter(notificationToEmailConverterCommon, approved); + this.putMembershipNotificationToMetricConverter = new PutGroupMembershipDecisionNotificationToMetricConverter(); + this.dbService = dbService; + } + + @Override + public List getNotifications() { + if (details == null) { + return new ArrayList<>(); + } + + // we need to send the notification to both the member whose pending membership was approved or rejected + // and also the member who requested the pending member + + List members = new ArrayList<>(); + members.add(details.getOrDefault(NOTIFICATION_DETAILS_MEMBER, "")); + members.add(details.getOrDefault(NOTIFICATION_DETAILS_REQUESTER, "")); + + MembershipDecisionNotificationCommon membershipDecisionNotificationCommon + = new MembershipDecisionNotificationCommon(dbService, domainRoleMembersFetcher, userDomainPrefix); + Set recipients = membershipDecisionNotificationCommon.getRecipients(members); + + return Collections.singletonList(notificationCommon.createNotification( + Notification.Type.GROUP_MEMBER_DECISION, + recipients, + details, + putMembershipNotificationToEmailConverter, + putMembershipNotificationToMetricConverter)); + } + + @Override + public String getDescription() { + return DESCRIPTION; + } + + public static class PutGroupMembershipDecisionNotificationToEmailConverter implements NotificationToEmailConverter { + private static final String EMAIL_TEMPLATE_NOTIFICATION_APPROVAL = "messages/pending-group-membership-approve.html"; + private static final String PENDING_MEMBERSHIP_APPROVAL_SUBJECT = "athenz.notification.email.pending_group_membership.decision.approval.subject"; + + private static final String EMAIL_TEMPLATE_NOTIFICATION_REJECT = "messages/pending-group-membership-reject.html"; + private static final String PENDING_MEMBERSHIP_REJECT_SUBJECT = "athenz.notification.email.pending_group_membership.decision.reject.subject"; + + private final NotificationToEmailConverterCommon notificationToEmailConverterCommon; + private final String emailMembershipDecisionBody; + private final boolean pendingMemberApproved; + + public PutGroupMembershipDecisionNotificationToEmailConverter( + NotificationToEmailConverterCommon notificationToEmailConverterCommon, boolean approved) { + this.notificationToEmailConverterCommon = notificationToEmailConverterCommon; + pendingMemberApproved = approved; + emailMembershipDecisionBody = getEmailBody(); + } + + String getMembershipDecisionBody(Map metaDetails) { + if (metaDetails == null) { + return null; + } + String athenzUIUrl = notificationToEmailConverterCommon.getAthenzUIUrl(); + String body = MessageFormat.format(emailMembershipDecisionBody, metaDetails.get(NOTIFICATION_DETAILS_DOMAIN), + metaDetails.get(NOTIFICATION_DETAILS_GROUP), metaDetails.get(NOTIFICATION_DETAILS_MEMBER), + metaDetails.get(NOTIFICATION_DETAILS_REASON), metaDetails.get(NOTIFICATION_DETAILS_REQUESTER), + metaDetails.get(NOTIFICATION_DETAILS_PENDING_MEMBERSHIP_STATE), + metaDetails.get(NOTIFICATION_DETAILS_PENDING_MEMBERSHIP_DECISION_PRINCIPAL), + athenzUIUrl); + return notificationToEmailConverterCommon.addCssStyleToBody(body); + } + + @Override + public NotificationEmail getNotificationAsEmail(Notification notification) { + String subject = notificationToEmailConverterCommon.getSubject(getNotificationSubjectProp()); + String body = getMembershipDecisionBody(notification.getDetails()); + Set fullyQualifiedEmailAddresses = + notificationToEmailConverterCommon.getFullyQualifiedEmailAddresses(notification.getRecipients()); + return new NotificationEmail(subject, body, fullyQualifiedEmailAddresses); + } + + String getEmailBody() { + if (pendingMemberApproved) { + return notificationToEmailConverterCommon.readContentFromFile(getClass().getClassLoader(), + EMAIL_TEMPLATE_NOTIFICATION_APPROVAL); + } else { + return notificationToEmailConverterCommon.readContentFromFile(getClass().getClassLoader(), + EMAIL_TEMPLATE_NOTIFICATION_REJECT); + } + } + + String getNotificationSubjectProp() { + if (pendingMemberApproved) { + return PENDING_MEMBERSHIP_APPROVAL_SUBJECT; + } else { + return PENDING_MEMBERSHIP_REJECT_SUBJECT; + } + } + } + + public static class PutGroupMembershipDecisionNotificationToMetricConverter implements NotificationToMetricConverter { + private final static String NOTIFICATION_TYPE = "pending_group_membership_decision"; + + @Override + public NotificationMetric getNotificationAsMetrics(Notification notification, Timestamp currentTime) { + String[] record = new String[] { + METRIC_NOTIFICATION_TYPE_KEY, NOTIFICATION_TYPE, + METRIC_NOTIFICATION_DOMAIN_KEY, notification.getDetails().get(NOTIFICATION_DETAILS_DOMAIN), + METRIC_NOTIFICATION_GROUP_KEY, notification.getDetails().get(NOTIFICATION_DETAILS_GROUP), + METRIC_NOTIFICATION_MEMBER_KEY, notification.getDetails().get(NOTIFICATION_DETAILS_MEMBER), + METRIC_NOTIFICATION_REASON_KEY, notification.getDetails().get(NOTIFICATION_DETAILS_REASON), + METRIC_NOTIFICATION_REQUESTER_KEY, notification.getDetails().get(NOTIFICATION_DETAILS_REQUESTER), + METRIC_NOTIFICATION_MEMBERSHIP_DECISION, notification.getDetails().get(NOTIFICATION_DETAILS_PENDING_MEMBERSHIP_DECISION) + }; + + List attributes = new ArrayList<>(); + attributes.add(record); + return new NotificationMetric(attributes); + } + } +} diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/notification/PutRoleMembershipDecisionNotificationTask.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/notification/PutRoleMembershipDecisionNotificationTask.java new file mode 100644 index 00000000000..571c17ab173 --- /dev/null +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/notification/PutRoleMembershipDecisionNotificationTask.java @@ -0,0 +1,163 @@ +/* + * Copyright The Athenz Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.yahoo.athenz.zms.notification; + +import com.yahoo.athenz.common.server.notification.*; +import com.yahoo.athenz.zms.DBService; +import com.yahoo.rdl.Timestamp; + +import java.text.MessageFormat; +import java.util.*; + +import static com.yahoo.athenz.common.server.notification.NotificationServiceConstants.*; +import static com.yahoo.athenz.common.server.notification.impl.MetricNotificationService.*; + +public class PutRoleMembershipDecisionNotificationTask implements NotificationTask { + + private final Map details; + private final NotificationCommon notificationCommon; + private final static String DESCRIPTION = "Pending Membership Decision Notification"; + private final PutRoleMembershipDecisionNotificationToEmailConverter putMembershipNotificationToEmailConverter; + private final PutRoleMembershipDecisionNotificationToMetricConverter putMembershipNotificationToMetricConverter; + private final DBService dbService; + private final DomainRoleMembersFetcher domainRoleMembersFetcher; + private final String userDomainPrefix; + + public PutRoleMembershipDecisionNotificationTask(Map details, Boolean approved, DBService dbService, + String userDomainPrefix, NotificationToEmailConverterCommon notificationToEmailConverterCommon) { + this.details = details; + this.userDomainPrefix = userDomainPrefix; + this.domainRoleMembersFetcher = new DomainRoleMembersFetcher(dbService, userDomainPrefix); + this.notificationCommon = new NotificationCommon(domainRoleMembersFetcher, userDomainPrefix); + this.putMembershipNotificationToEmailConverter = + new PutRoleMembershipDecisionNotificationToEmailConverter(notificationToEmailConverterCommon, approved); + this.putMembershipNotificationToMetricConverter = new PutRoleMembershipDecisionNotificationToMetricConverter(); + this.dbService = dbService; + } + + @Override + public List getNotifications() { + + if (details == null) { + return new ArrayList<>(); + } + + // we need to send the notification to both the member whose pending membership was approved or rejected + // and also the member who requested the pending member + + List members = new ArrayList<>(); + members.add(details.getOrDefault(NOTIFICATION_DETAILS_MEMBER, "")); + members.add(details.getOrDefault(NOTIFICATION_DETAILS_REQUESTER, "")); + + MembershipDecisionNotificationCommon membershipDecisionNotificationCommon + = new MembershipDecisionNotificationCommon(dbService, domainRoleMembersFetcher, userDomainPrefix); + Set recipients = membershipDecisionNotificationCommon.getRecipients(members); + + return Collections.singletonList(notificationCommon.createNotification( + Notification.Type.ROLE_MEMBER_DECISION, + recipients, + details, + putMembershipNotificationToEmailConverter, + putMembershipNotificationToMetricConverter)); + } + + @Override + public String getDescription() { + return DESCRIPTION; + } + + public static class PutRoleMembershipDecisionNotificationToEmailConverter implements NotificationToEmailConverter { + private static final String EMAIL_TEMPLATE_NOTIFICATION_APPROVAL = "messages/pending-role-membership-approve.html"; + private static final String PENDING_MEMBERSHIP_APPROVAL_SUBJECT = "athenz.notification.email.pending_role_membership.decision.approval.subject"; + + private static final String EMAIL_TEMPLATE_NOTIFICATION_REJECT = "messages/pending-role-membership-reject.html"; + private static final String PENDING_MEMBERSHIP_REJECT_SUBJECT = "athenz.notification.email.pending_role_membership.decision.reject.subject"; + + private final NotificationToEmailConverterCommon notificationToEmailConverterCommon; + private final String emailMembershipDecisionBody; + private final boolean pendingMemberApproved; + + public PutRoleMembershipDecisionNotificationToEmailConverter( + NotificationToEmailConverterCommon notificationToEmailConverterCommon, boolean approved) { + this.notificationToEmailConverterCommon = notificationToEmailConverterCommon; + pendingMemberApproved = approved; + emailMembershipDecisionBody = getEmailBody(); + } + + String getMembershipDecisionBody(Map metaDetails) { + if (metaDetails == null) { + return null; + } + String athenzUIUrl = notificationToEmailConverterCommon.getAthenzUIUrl(); + String body = MessageFormat.format(emailMembershipDecisionBody, metaDetails.get(NOTIFICATION_DETAILS_DOMAIN), + metaDetails.get(NOTIFICATION_DETAILS_ROLE), metaDetails.get(NOTIFICATION_DETAILS_MEMBER), + metaDetails.get(NOTIFICATION_DETAILS_REASON), metaDetails.get(NOTIFICATION_DETAILS_REQUESTER), + metaDetails.get(NOTIFICATION_DETAILS_PENDING_MEMBERSHIP_STATE), + metaDetails.get(NOTIFICATION_DETAILS_PENDING_MEMBERSHIP_DECISION_PRINCIPAL), + athenzUIUrl); + return notificationToEmailConverterCommon.addCssStyleToBody(body); + } + + @Override + public NotificationEmail getNotificationAsEmail(Notification notification) { + String subject = notificationToEmailConverterCommon.getSubject(getNotificationSubjectProp()); + String body = getMembershipDecisionBody(notification.getDetails()); + Set fullyQualifiedEmailAddresses = + notificationToEmailConverterCommon.getFullyQualifiedEmailAddresses(notification.getRecipients()); + return new NotificationEmail(subject, body, fullyQualifiedEmailAddresses); + } + + String getEmailBody() { + if (pendingMemberApproved) { + return notificationToEmailConverterCommon.readContentFromFile(getClass().getClassLoader(), + EMAIL_TEMPLATE_NOTIFICATION_APPROVAL); + } else { + return notificationToEmailConverterCommon.readContentFromFile(getClass().getClassLoader(), + EMAIL_TEMPLATE_NOTIFICATION_REJECT); + } + } + + String getNotificationSubjectProp() { + if (pendingMemberApproved) { + return PENDING_MEMBERSHIP_APPROVAL_SUBJECT; + } else { + return PENDING_MEMBERSHIP_REJECT_SUBJECT; + } + } + } + + public static class PutRoleMembershipDecisionNotificationToMetricConverter implements NotificationToMetricConverter { + private final static String NOTIFICATION_TYPE = "pending_role_membership_decision"; + + @Override + public NotificationMetric getNotificationAsMetrics(Notification notification, Timestamp currentTime) { + String[] record = new String[] { + METRIC_NOTIFICATION_TYPE_KEY, NOTIFICATION_TYPE, + METRIC_NOTIFICATION_DOMAIN_KEY, notification.getDetails().get(NOTIFICATION_DETAILS_DOMAIN), + METRIC_NOTIFICATION_ROLE_KEY, notification.getDetails().get(NOTIFICATION_DETAILS_ROLE), + METRIC_NOTIFICATION_MEMBER_KEY, notification.getDetails().get(NOTIFICATION_DETAILS_MEMBER), + METRIC_NOTIFICATION_REASON_KEY, notification.getDetails().get(NOTIFICATION_DETAILS_REASON), + METRIC_NOTIFICATION_REQUESTER_KEY, notification.getDetails().get(NOTIFICATION_DETAILS_REQUESTER), + METRIC_NOTIFICATION_MEMBERSHIP_DECISION, notification.getDetails().get(NOTIFICATION_DETAILS_PENDING_MEMBERSHIP_DECISION) + }; + + List attributes = new ArrayList<>(); + attributes.add(record); + return new NotificationMetric(attributes); + } + } +} diff --git a/servers/zms/src/main/resources/messages/pending-group-membership-approve.html b/servers/zms/src/main/resources/messages/pending-group-membership-approve.html new file mode 100644 index 00000000000..2992ebeef4c --- /dev/null +++ b/servers/zms/src/main/resources/messages/pending-group-membership-approve.html @@ -0,0 +1,34 @@ + + + + + + + + +
+
+ +
Pending Group Membership Approved Details
+
Please find below the details of the decision regarding the pending group member:
+
+
+ + + + + + + + +
DOMAIN{0}
GROUP{1}
MEMBER{2}
REASON{3}
REQUESTER{4}
PENDING OPERATION{5}
APPROVED BY{6}
+
+
+ +
+ + diff --git a/servers/zms/src/main/resources/messages/pending-group-membership-reject.html b/servers/zms/src/main/resources/messages/pending-group-membership-reject.html new file mode 100644 index 00000000000..4cb14246efa --- /dev/null +++ b/servers/zms/src/main/resources/messages/pending-group-membership-reject.html @@ -0,0 +1,34 @@ + + + + + + + + +
+
+ +
Pending Group Membership Rejected Details
+
Please find below the details of the decision regarding the pending group member:
+
+
+ + + + + + + + +
DOMAIN{0}
GROUP{1}
MEMBER{2}
REASON{3}
REQUESTER{4}
PENDING OPERATION{5}
REJECTED BY{6}
+
+
+ +
+ + diff --git a/servers/zms/src/main/resources/messages/pending-role-membership-approve.html b/servers/zms/src/main/resources/messages/pending-role-membership-approve.html new file mode 100644 index 00000000000..31ed6e9b59a --- /dev/null +++ b/servers/zms/src/main/resources/messages/pending-role-membership-approve.html @@ -0,0 +1,34 @@ + + + + + + + + +
+
+ +
Pending Membership Approved Details
+
Please find below the details of the decision regarding the pending role member:
+
+
+ + + + + + + + +
DOMAIN{0}
ROLE{1}
MEMBER{2}
REASON{3}
REQUESTER{4}
PENDING OPERATION{5}
APPROVED BY{6}
+
+
+ +
+ + diff --git a/servers/zms/src/main/resources/messages/pending-role-membership-reject.html b/servers/zms/src/main/resources/messages/pending-role-membership-reject.html new file mode 100644 index 00000000000..9188a8e3a75 --- /dev/null +++ b/servers/zms/src/main/resources/messages/pending-role-membership-reject.html @@ -0,0 +1,34 @@ + + + + + + + + +
+
+ +
Pending Membership Rejected Details
+
Please find below the details of the decision regarding the pending role member:
+
+
+ + + + + + + + +
DOMAIN{0}
ROLE{1}
MEMBER{2}
REASON{3}
REQUESTER{4}
PENDING OPERATION{5}
REJECTED BY{6}
+
+
+ +
+ + diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/DBServiceTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/DBServiceTest.java index b084822f588..795d28dd26c 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/DBServiceTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/DBServiceTest.java @@ -13378,4 +13378,145 @@ public void testProcessUpdateRoleMembersDeleteRoleMembers() throws ServerResourc assertTrue(zms.dbService.processUpdateRoleMembers(conn, originalRole, new ArrayList<>(), false, domainName, roleName, "user.admin", auditRef, auditDetails)); } + + @Test + public void testGetPendingRoleMember() throws ServerResourceException { + + String domainName = "domain1"; + String roleName = "role1"; + String memberName = "user.user1"; + String requestPrincipal = "user.joe"; + + RoleMember dummyResult = new RoleMember(); + dummyResult.setPendingState(PENDING_REQUEST_ADD_STATE); + dummyResult.setMemberName(memberName); + dummyResult.setRequestPrincipal(requestPrincipal); + Mockito.when(mockJdbcConn.getPendingRoleMember(domainName, roleName, memberName)).thenReturn(dummyResult); + + ObjectStore saveStore = zms.dbService.store; + zms.dbService.store = mockObjStore; + + RoleMember roleMember = zms.dbService.getPendingRoleMember(domainName, roleName, memberName); + assertNotNull(roleMember); + assertEquals(roleMember.getMemberName(), memberName); + assertEquals(roleMember.getRequestPrincipal(), requestPrincipal); + assertEquals(roleMember.getPendingState(), PENDING_REQUEST_ADD_STATE); + assertNull(roleMember.getExpiration()); + assertNull(roleMember.getReviewReminder()); + zms.dbService.store = saveStore; + } + + @Test + public void testGetPendingRoleMemberNotFound() throws ServerResourceException { + + String domainName = "domain1"; + String roleName = "role1"; + String memberName = "user.user1"; + + Mockito.when(mockJdbcConn.getPendingRoleMember(domainName, roleName, memberName)).thenReturn(null); + ObjectStore saveStore = zms.dbService.store; + zms.dbService.store = mockObjStore; + + try { + zms.dbService.getPendingRoleMember(domainName, roleName, memberName); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.NOT_FOUND); + } + zms.dbService.store = saveStore; + } + + @Test + public void testGetPendingRoleMemberException() throws ServerResourceException { + + String domainName = "domain1"; + String roleName = "role1"; + String memberName = "user.user1"; + + Mockito.when(mockJdbcConn.getPendingRoleMember(domainName, roleName, memberName)) + .thenThrow(new ServerResourceException(ServerResourceException.INTERNAL_SERVER_ERROR)); + ObjectStore saveStore = zms.dbService.store; + zms.dbService.store = mockObjStore; + + try { + zms.dbService.getPendingRoleMember(domainName, roleName, memberName); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.INTERNAL_SERVER_ERROR); + } + zms.dbService.store = saveStore; + } + + @Test + public void testGetPendingGroupMember() throws ServerResourceException { + + String domainName = "domain1"; + String groupName = "group1"; + String memberName = "user.user1"; + String requestPrincipal = "user.joe"; + + GroupMember dummyResult = new GroupMember(); + dummyResult.setPendingState(PENDING_REQUEST_ADD_STATE); + dummyResult.setMemberName(memberName); + dummyResult.setRequestPrincipal(requestPrincipal); + + Mockito.when(mockJdbcConn.getPendingGroupMember(domainName, groupName, memberName)).thenReturn(dummyResult); + + ObjectStore saveStore = zms.dbService.store; + zms.dbService.store = mockObjStore; + + GroupMember groupMember = zms.dbService.getPendingGroupMember(domainName, groupName, memberName); + assertNotNull(groupMember); + assertEquals(groupMember.getMemberName(), memberName); + assertEquals(groupMember.getRequestPrincipal(), requestPrincipal); + assertEquals(groupMember.getPendingState(), PENDING_REQUEST_ADD_STATE); + assertNull(groupMember.getExpiration()); + + zms.dbService.store = saveStore; + } + + @Test + public void testGetPendingGroupMemberNotFound() throws ServerResourceException { + + String domainName = "domain1"; + String groupName = "group1"; + String memberName = "user.user1"; + + Mockito.when(mockJdbcConn.getPendingGroupMember(domainName, groupName, memberName)).thenReturn(null); + + ObjectStore saveStore = zms.dbService.store; + zms.dbService.store = mockObjStore; + + try { + zms.dbService.getPendingGroupMember(domainName, groupName, memberName); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.NOT_FOUND); + } + + zms.dbService.store = saveStore; + } + + @Test + public void testGetPendingGroupMemberException() throws ServerResourceException { + + String domainName = "domain1"; + String groupName = "group1"; + String memberName = "user.user1"; + + Mockito.when(mockJdbcConn.getPendingGroupMember(domainName, groupName, memberName)) + .thenThrow(new ServerResourceException(ServerResourceException.INTERNAL_SERVER_ERROR)); + + ObjectStore saveStore = zms.dbService.store; + zms.dbService.store = mockObjStore; + + try { + zms.dbService.getPendingGroupMember(domainName, groupName, memberName); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.INTERNAL_SERVER_ERROR); + } + + zms.dbService.store = saveStore; + } } diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSImplTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSImplTest.java index 815a6eb54e1..ff8b8a5489e 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSImplTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSImplTest.java @@ -49,6 +49,7 @@ import com.yahoo.athenz.zms.ZMSImpl.AccessStatus; import com.yahoo.athenz.zms.ZMSImpl.AthenzObject; import com.yahoo.athenz.zms.config.MemberDueDays; +import com.yahoo.athenz.zms.notification.PutRoleMembershipDecisionNotificationTask; import com.yahoo.athenz.zms.notification.PutRoleMembershipNotificationTask; import com.yahoo.athenz.zms.provider.ServiceProviderManager; import com.yahoo.athenz.zms.status.MockStatusCheckerNoException; @@ -23764,6 +23765,73 @@ public void testGetJWSDomainError() { zmsImpl.privateKey = pkey; } + @Test + public void testGetJWSDomainResourceOwnership() throws JsonProcessingException, ParseException, JOSEException { + + final String domainName = "jws-domain-resource-owner"; + + ZMSImpl zmsImpl = zmsTestInitializer.getZms(); + RsrcCtxWrapper ctx = zmsTestInitializer.getMockDomRsrcCtx(); + final String auditRef = zmsTestInitializer.getAuditRef(); + + TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject(domainName, + "Test Domain1", "testOrg", zmsTestInitializer.getAdminUser()); + dom1.setMemberPurgeExpiryDays(90); + + zmsImpl.postTopLevelDomain(ctx, auditRef, "unit-test", dom1); + + Response response = zmsImpl.getJWSDomain(ctx, domainName, null, null); + JWSDomain jwsDomain = (JWSDomain) response.getEntity(); + DomainData domainData = zmsTestInitializer.getDomainData(jwsDomain); + + assertNotNull(domainData); + assertEquals(domainData.getName(), "jws-domain-resource-owner"); + assertEquals(domainData.getMemberPurgeExpiryDays(), 90); + assertNotNull(domainData.getResourceOwnership()); + assertEquals(domainData.getResourceOwnership().getObjectOwner(), "unit-test"); + assertEquals(domainData.getResourceOwnership().getMetaOwner(), "unit-test"); + + Map header = jwsDomain.getHeader(); + assertEquals(header.get("kid"), "0"); + + // now we're going to ask for the same domain with the tag + // and make sure we get back 304 + + EntityTag tag = response.getEntityTag(); + response = zmsImpl.getJWSDomain(ctx, domainName, Boolean.FALSE, tag.getValue()); + assertEquals(response.getStatus(), ResourceException.NOT_MODIFIED); + + // pass a timestamp a minute back and make sure we + // get back the domain + + Timestamp tstamp = Timestamp.fromMillis(System.currentTimeMillis() - 3600); + response = zmsImpl.getJWSDomain(ctx, domainName, false, tstamp.toString()); + jwsDomain = (JWSDomain) response.getEntity(); + domainData = zmsTestInitializer.getDomainData(jwsDomain); + + assertNotNull(domainData); + assertEquals(domainData.getName(), "jws-domain-resource-owner"); + assertEquals(domainData.getMemberPurgeExpiryDays(), 90); + assertNotNull(domainData.getResourceOwnership()); + assertEquals(domainData.getResourceOwnership().getObjectOwner(), "unit-test"); + assertEquals(domainData.getResourceOwnership().getMetaOwner(), "unit-test"); + + // any invalid data is also treated as no etag + + response = zmsImpl.getJWSDomain(ctx, domainName, null, "unknown-date"); + jwsDomain = (JWSDomain) response.getEntity(); + domainData = zmsTestInitializer.getDomainData(jwsDomain); + + assertNotNull(domainData); + assertEquals(domainData.getName(), "jws-domain-resource-owner"); + assertEquals(domainData.getMemberPurgeExpiryDays(), 90); + assertNotNull(domainData.getResourceOwnership()); + assertEquals(domainData.getResourceOwnership().getObjectOwner(), "unit-test"); + assertEquals(domainData.getResourceOwnership().getMetaOwner(), "unit-test"); + + zmsImpl.deleteTopLevelDomain(ctx, domainName, auditRef, "unit-test"); + } + @Test public void testValidateIntegerValue() { @@ -30799,4 +30867,354 @@ public void testPutGroupReviewAuthorization() { zmsImpl.deleteTopLevelDomain(ctx, domainName, auditRef, null); } + + @Test + public void testPutRoleMembershipApproveDecisionNotification() { + + ZMSImpl zmsImpl = zmsTestInitializer.getZms(); + RsrcCtxWrapper ctx = zmsTestInitializer.getMockDomRsrcCtx(); + final String auditRef = zmsTestInitializer.getAuditRef(); + + final String domainName = "pending-mbr-approve-decision-notif"; + TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject(domainName, "Approval test Domain1", + "testOrg", "user.user1"); + dom1.getAdminUsers().add("user.user2"); + zmsImpl.postTopLevelDomain(ctx, auditRef, null, dom1); + + final String roleName = "review-role"; + Role role1 = zmsTestInitializer.createRoleObject(domainName, roleName, null, null, null); + zmsImpl.putRole(ctx, domainName, roleName, auditRef, false, null, role1); + RoleMeta rm = new RoleMeta().setReviewEnabled(true); + zmsImpl.putRoleMeta(ctx, domainName, roleName, auditRef, null, rm); + + // switch to user.user2 principal to add a member to a role + + Authority principalAuthority = new com.yahoo.athenz.common.server.debug.DebugPrincipalAuthority(); + String unsignedCreds = "v=U1;d=user;n=user2"; + final Principal rsrcPrince = SimplePrincipal.create("user", "user2", + unsignedCreds + ";s=signature", 0, principalAuthority); + assertNotNull(rsrcPrince); + ((SimplePrincipal) rsrcPrince).setUnsignedCreds(unsignedCreds); + when(zmsTestInitializer.getMockDomRestRsrcCtx().principal()).thenReturn(rsrcPrince); + when(ctx.principal()).thenReturn(rsrcPrince); + + Membership mbr = new Membership(); + mbr.setMemberName("user.bob"); + mbr.setActive(false); + mbr.setApproved(false); + zmsImpl.putMembership(ctx, domainName, roleName, "user.bob", auditRef, false, null, mbr); + + // verify the user is added with pending state + + Role resrole = zmsImpl.getRole(ctx, domainName, roleName, false, false, true); + assertEquals(resrole.getRoleMembers().size(), 1); + assertEquals(resrole.getRoleMembers().get(0).getMemberName(), "user.bob"); + assertEquals(resrole.getRoleMembers().get(0).getPendingState(), PENDING_REQUEST_ADD_STATE); + assertFalse(resrole.getRoleMembers().get(0).getApproved()); + Mockito.clearInvocations(zmsTestInitializer.getMockNotificationManager()); + + // revert back to admin principal + + Authority adminPrincipalAuthority = new com.yahoo.athenz.common.server.debug.DebugPrincipalAuthority(); + String adminUnsignedCreds = "v=U1;d=user;n=user1"; + final Principal rsrcAdminPrince = SimplePrincipal.create("user", "user1", + adminUnsignedCreds + ";s=signature", 0, adminPrincipalAuthority); + assertNotNull(rsrcAdminPrince); + ((SimplePrincipal) rsrcAdminPrince).setUnsignedCreds(adminUnsignedCreds); + when(zmsTestInitializer.getMockDomRestRsrcCtx().principal()).thenReturn(rsrcAdminPrince); + when(ctx.principal()).thenReturn(rsrcAdminPrince); + + // approve the message which should be successful + + mbr = new Membership(); + mbr.setMemberName("user.bob"); + mbr.setActive(true); + mbr.setApproved(true); + zmsImpl.putMembershipDecision(ctx, domainName, roleName, "user.bob", auditRef, mbr); + + // verify user is active + + resrole = zmsImpl.getRole(ctx, domainName, roleName, false, false, true); + assertEquals(resrole.getRoleMembers().size(), 1); + assertEquals(resrole.getRoleMembers().get(0).getMemberName(), "user.bob"); + assertTrue(resrole.getRoleMembers().get(0).getApproved()); + List expectedNotifications = Collections.singletonList( + new Notification(Notification.Type.ROLE_MEMBER_DECISION) + .addRecipient("user.bob") + .addRecipient("user.user2") + .addDetails("requester", "user.user2") + .addDetails("reason", auditRef) + .addDetails("role", "review-role") + .addDetails("domain", domainName) + .addDetails("member", "user.bob") + .addDetails("pendingState", "ADD") + .addDetails("actionPrincipal", "user.user1") + .addDetails("membershipDecision", "approve") + .setNotificationToEmailConverter( + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter( + new NotificationToEmailConverterCommon(null), true)) + .setNotificationToMetricConverter( + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToMetricConverter())); + verify(zmsTestInitializer.getMockNotificationManager(), + times(1)).sendNotifications(eq(expectedNotifications)); + zmsImpl.deleteTopLevelDomain(ctx, domainName, auditRef, null); + } + + @Test + public void testPutRoleMembershipRejectDecisionNotification() { + + ZMSImpl zmsImpl = zmsTestInitializer.getZms(); + RsrcCtxWrapper ctx = zmsTestInitializer.getMockDomRsrcCtx(); + final String auditRef = zmsTestInitializer.getAuditRef(); + final String domainName = "pending-mbr-reject-decision-notif"; + + TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject(domainName, "Approval test Domain1", + "testOrg", "user.user1"); + dom1.getAdminUsers().add("user.user2"); + zmsImpl.postTopLevelDomain(ctx, auditRef, null, dom1); + + final String roleName = "review-role"; + Role role1 = zmsTestInitializer.createRoleObject(domainName, roleName, null, null, null); + zmsImpl.putRole(ctx, domainName, roleName, auditRef, false, null, role1); + RoleMeta rm = new RoleMeta().setReviewEnabled(true); + zmsImpl.putRoleMeta(ctx, domainName, roleName, auditRef, null, rm); + + // switch to user.user2 principal to add a member to a role + + Authority principalAuthority = new com.yahoo.athenz.common.server.debug.DebugPrincipalAuthority(); + String unsignedCreds = "v=U1;d=user;n=user2"; + final Principal rsrcPrince = SimplePrincipal.create("user", "user2", + unsignedCreds + ";s=signature", 0, principalAuthority); + assertNotNull(rsrcPrince); + ((SimplePrincipal) rsrcPrince).setUnsignedCreds(unsignedCreds); + when(zmsTestInitializer.getMockDomRestRsrcCtx().principal()).thenReturn(rsrcPrince); + when(ctx.principal()).thenReturn(rsrcPrince); + + Membership mbr = new Membership(); + mbr.setMemberName("user.bob"); + mbr.setActive(false); + mbr.setApproved(false); + zmsImpl.putMembership(ctx, domainName, roleName, "user.bob", auditRef, false, null, mbr); + + // verify the user is added with pending state + + Role resrole = zmsImpl.getRole(ctx, domainName, roleName, false, false, true); + assertEquals(resrole.getRoleMembers().size(), 1); + assertEquals(resrole.getRoleMembers().get(0).getMemberName(), "user.bob"); + assertEquals(resrole.getRoleMembers().get(0).getPendingState(), PENDING_REQUEST_ADD_STATE); + assertFalse(resrole.getRoleMembers().get(0).getApproved()); + Mockito.clearInvocations(zmsTestInitializer.getMockNotificationManager()); + + // revert back to admin principal + + Authority adminPrincipalAuthority = new com.yahoo.athenz.common.server.debug.DebugPrincipalAuthority(); + String adminUnsignedCreds = "v=U1;d=user;n=user1"; + final Principal rsrcAdminPrince = SimplePrincipal.create("user", "user1", + adminUnsignedCreds + ";s=signature", 0, adminPrincipalAuthority); + assertNotNull(rsrcAdminPrince); + ((SimplePrincipal) rsrcAdminPrince).setUnsignedCreds(adminUnsignedCreds); + when(zmsTestInitializer.getMockDomRestRsrcCtx().principal()).thenReturn(rsrcAdminPrince); + when(ctx.principal()).thenReturn(rsrcAdminPrince); + + // reject the message which should be successful + + mbr = new Membership(); + mbr.setMemberName("user.bob"); + mbr.setActive(false); + mbr.setApproved(false); + zmsImpl.putMembershipDecision(ctx, domainName, roleName, "user.bob", auditRef, mbr); + + // verify user is not active + + resrole = zmsImpl.getRole(ctx, domainName, roleName, false, false, true); + assertEquals(resrole.getRoleMembers().size(), 0); + List expectedNotifications = Collections.singletonList( + new Notification(Notification.Type.ROLE_MEMBER_DECISION) + .addRecipient("user.bob") + .addRecipient("user.user2") + .addDetails("requester", "user.user2") + .addDetails("reason", auditRef) + .addDetails("role", "review-role") + .addDetails("domain", domainName) + .addDetails("member", "user.bob") + .addDetails("pendingState", "ADD") + .addDetails("actionPrincipal", "user.user1") + .addDetails("membershipDecision", "reject") + .setNotificationToEmailConverter( + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter( + new NotificationToEmailConverterCommon(null), false)) + .setNotificationToMetricConverter( + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToMetricConverter())); + verify(zmsTestInitializer.getMockNotificationManager(), + times(1)).sendNotifications(eq(expectedNotifications)); + zmsImpl.deleteTopLevelDomain(ctx, domainName, auditRef, null); + } + + @Test + public void testDeletePutRoleMembershipApproveDecisionNotification() { + + ZMSImpl zmsImpl = zmsTestInitializer.getZms(); + RsrcCtxWrapper ctx = zmsTestInitializer.getMockDomRsrcCtx(); + final String auditRef = zmsTestInitializer.getAuditRef(); + final String domainName = "delete-pending-mbr-decision-notif"; + + TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject(domainName, "Approval test Domain1", + "testOrg", "user.user1"); + dom1.getAdminUsers().add("user.user2"); + zmsImpl.postTopLevelDomain(ctx, auditRef, null, dom1); + + Role role1 = zmsTestInitializer.createRoleObject(domainName, "role1", null, + "user.joe", null); + Response response = zmsImpl.putRole(ctx, domainName, "role1", auditRef, true, null, role1); + Role role = (Role) response.getEntity(); + assertEquals(role.getRoleMembers().size(), 1); + RoleMeta meta = new RoleMeta().setReviewEnabled(true).setDeleteProtection(true); + zmsImpl.putRoleMeta(ctx, domainName, "role1", auditRef, null, meta); + + // switch to user2 for delete membership + + Authority principalAuthority = new com.yahoo.athenz.common.server.debug.DebugPrincipalAuthority(); + String unsignedCreds = "v=U1;d=user;n=user2"; + final Principal rsrcPrince = SimplePrincipal.create("user", "user2", + unsignedCreds + ";s=signature", 0, principalAuthority); + assertNotNull(rsrcPrince); + ((SimplePrincipal) rsrcPrince).setUnsignedCreds(unsignedCreds); + when(zmsTestInitializer.getMockDomRestRsrcCtx().principal()).thenReturn(rsrcPrince); + when(ctx.principal()).thenReturn(rsrcPrince); + zmsImpl.deleteMembership(ctx, domainName, "role1", "user.joe", auditRef, null); + + // verify user is present + + Role resrole = zmsImpl.getRole(ctx, domainName, "role1", false, false, true); + assertEquals(resrole.getRoleMembers().size(), 2); + assertEquals(resrole.getRoleMembers().get(0).getMemberName(), "user.joe"); + + // revert back to admin principal + + Authority adminPrincipalAuthority = new com.yahoo.athenz.common.server.debug.DebugPrincipalAuthority(); + String adminUnsignedCreds = "v=U1;d=user;n=user1"; + final Principal rsrcAdminPrince = SimplePrincipal.create("user", "user1", + adminUnsignedCreds + ";s=signature", 0, adminPrincipalAuthority); + assertNotNull(rsrcAdminPrince); + ((SimplePrincipal) rsrcAdminPrince).setUnsignedCreds(adminUnsignedCreds); + when(zmsTestInitializer.getMockDomRestRsrcCtx().principal()).thenReturn(rsrcAdminPrince); + when(ctx.principal()).thenReturn(rsrcAdminPrince); + + // approve the message which should be successful + + Membership mbr = new Membership(); + mbr.setMemberName("user.joe"); + mbr.setActive(false); + mbr.setApproved(true); + zmsImpl.putMembershipDecision(ctx, domainName, "role1", "user.joe", auditRef, mbr); + + // verify user is not present + + resrole = zmsImpl.getRole(ctx, domainName, "role1", false, false, true); + assertEquals(resrole.getRoleMembers().size(), 0); + List expextedNotifications = Collections.singletonList( + new Notification(Notification.Type.ROLE_MEMBER_DECISION) + .addRecipient("user.joe") + .addRecipient("user.user2") + .addDetails("requester", "user.user2") + .addDetails("reason", auditRef) + .addDetails("role", "role1") + .addDetails("domain", domainName) + .addDetails("member", "user.joe") + .addDetails("pendingState", "DELETE") + .addDetails("actionPrincipal", "user.user1") + .addDetails("membershipDecision", "approve") + .setNotificationToEmailConverter( + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter( + new NotificationToEmailConverterCommon(null), true)) + .setNotificationToMetricConverter( + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToMetricConverter())); + verify(zmsTestInitializer.getMockNotificationManager(), + times(1)).sendNotifications(eq(expextedNotifications)); + zmsImpl.deleteTopLevelDomain(ctx, domainName, auditRef, null); + } + + @Test + public void testDeletePutRoleMembershipRejectDecisionNotification() { + + ZMSImpl zmsImpl = zmsTestInitializer.getZms(); + RsrcCtxWrapper ctx = zmsTestInitializer.getMockDomRsrcCtx(); + final String auditRef = zmsTestInitializer.getAuditRef(); + final String domainName = "delete-pending-mbr-reject-decision-notif"; + + TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject(domainName, "Approval test Domain1", + "testOrg", "user.user1"); + dom1.getAdminUsers().add("user.user2"); + zmsImpl.postTopLevelDomain(ctx, auditRef, null, dom1); + Role role1 = zmsTestInitializer.createRoleObject(domainName, "role1", null, + "user.joe", null); + Response response = zmsImpl.putRole(ctx, domainName, "role1", auditRef, true, null, role1); + + Role role = (Role) response.getEntity(); + assertEquals(role.getRoleMembers().size(), 1); + RoleMeta meta = new RoleMeta().setReviewEnabled(true).setDeleteProtection(true); + zmsImpl.putRoleMeta(ctx, domainName, "role1", auditRef, null, meta); + + // switch to user2 for delete membership + + Authority principalAuthority = new com.yahoo.athenz.common.server.debug.DebugPrincipalAuthority(); + String unsignedCreds = "v=U1;d=user;n=user2"; + final Principal rsrcPrince = SimplePrincipal.create("user", "user2", + unsignedCreds + ";s=signature", 0, principalAuthority); + assertNotNull(rsrcPrince); + ((SimplePrincipal) rsrcPrince).setUnsignedCreds(unsignedCreds); + when(zmsTestInitializer.getMockDomRestRsrcCtx().principal()).thenReturn(rsrcPrince); + when(ctx.principal()).thenReturn(rsrcPrince); + zmsImpl.deleteMembership(ctx, domainName, "role1", "user.joe", auditRef, null); + + // verify user is present + + Role resrole = zmsImpl.getRole(ctx, domainName, "role1", false, false, true); + assertEquals(resrole.getRoleMembers().size(), 2); + assertEquals(resrole.getRoleMembers().get(0).getMemberName(), "user.joe"); + + // revert back to admin principal + + Authority adminPrincipalAuthority = new com.yahoo.athenz.common.server.debug.DebugPrincipalAuthority(); + String adminUnsignedCreds = "v=U1;d=user;n=user1"; + final Principal rsrcAdminPrince = SimplePrincipal.create("user", "user1", + adminUnsignedCreds + ";s=signature", 0, adminPrincipalAuthority); + assertNotNull(rsrcAdminPrince); + ((SimplePrincipal) rsrcAdminPrince).setUnsignedCreds(adminUnsignedCreds); + when(zmsTestInitializer.getMockDomRestRsrcCtx().principal()).thenReturn(rsrcAdminPrince); + when(ctx.principal()).thenReturn(rsrcAdminPrince); + + // reject the message which should be successful + + Membership mbr = new Membership(); + mbr.setMemberName("user.joe"); + mbr.setActive(true); + mbr.setApproved(false); + zmsImpl.putMembershipDecision(ctx, domainName, "role1", "user.joe", auditRef, mbr); + + // verify user is not present + + resrole = zmsImpl.getRole(ctx, domainName, "role1", false, false, true); + assertEquals(resrole.getRoleMembers().size(), 1); + List expextedNotifications = Collections.singletonList( + new Notification(Notification.Type.ROLE_MEMBER_DECISION) + .addRecipient("user.joe") + .addRecipient("user.user2") + .addDetails("requester", "user.user2") + .addDetails("reason", auditRef) + .addDetails("role", "role1") + .addDetails("domain", domainName) + .addDetails("member", "user.joe") + .addDetails("pendingState", "DELETE") + .addDetails("actionPrincipal", "user.user1") + .addDetails("membershipDecision", "reject") + .setNotificationToEmailConverter( + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter( + new NotificationToEmailConverterCommon(null), false)) + .setNotificationToMetricConverter( + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToMetricConverter())); + verify(zmsTestInitializer.getMockNotificationManager(), + times(1)).sendNotifications(eq(expextedNotifications)); + zmsImpl.deleteTopLevelDomain(ctx, domainName, auditRef, null); + } } diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/DomainRoleMembersFetcherTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/DomainRoleMembersFetcherTest.java index 7ee87e686aa..8aa6d12f7c1 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/DomainRoleMembersFetcherTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/DomainRoleMembersFetcherTest.java @@ -31,8 +31,8 @@ import static com.yahoo.athenz.common.ServerCommonConsts.USER_DOMAIN_PREFIX; import static org.mockito.ArgumentMatchers.eq; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertTrue; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; public class DomainRoleMembersFetcherTest { @Test diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/MembershipDecisionNotificationCommonTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/MembershipDecisionNotificationCommonTest.java new file mode 100644 index 00000000000..de139369c39 --- /dev/null +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/MembershipDecisionNotificationCommonTest.java @@ -0,0 +1,194 @@ +/* + * Copyright The Athenz Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.yahoo.athenz.zms.notification; + +import com.yahoo.athenz.common.server.notification.DomainRoleMembersFetcher; +import com.yahoo.athenz.zms.DBService; +import com.yahoo.athenz.zms.Group; +import com.yahoo.athenz.zms.Role; +import com.yahoo.athenz.zms.RoleMember; +import org.mockito.Mockito; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import static com.yahoo.athenz.common.ServerCommonConsts.USER_DOMAIN_PREFIX; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.assertEquals; + +public class MembershipDecisionNotificationCommonTest { + + @Test + public void testGetRecipientsUser() { + DBService dbsvc = Mockito.mock(DBService.class); + + DomainRoleMembersFetcher domainRoleMembersFetcher = new DomainRoleMembersFetcher(dbsvc, USER_DOMAIN_PREFIX); + MembershipDecisionNotificationCommon membershipDecisionNotificationCommon = + new MembershipDecisionNotificationCommon(dbsvc, domainRoleMembersFetcher, USER_DOMAIN_PREFIX); + + List members = new ArrayList<>(); + members.add("user.joe"); + members.add("user.jane"); + Set recipients = membershipDecisionNotificationCommon.getRecipients(members); + + assertEquals(2, recipients.size()); + assertTrue(recipients.contains("user.joe")); + assertTrue(recipients.contains("user.jane")); + } + + @Test + public void testGetRecipientsService() { + DBService dbsvc = Mockito.mock(DBService.class); + + DomainRoleMembersFetcher domainRoleMembersFetcher = new DomainRoleMembersFetcher(dbsvc, USER_DOMAIN_PREFIX); + MembershipDecisionNotificationCommon membershipDecisionNotificationCommon = + new MembershipDecisionNotificationCommon(dbsvc, domainRoleMembersFetcher, USER_DOMAIN_PREFIX); + + List roleMembers = new ArrayList<>(); + RoleMember rm = new RoleMember().setMemberName("user.approver1").setActive(true); + roleMembers.add(rm); + + rm = new RoleMember().setMemberName("user.approver2").setActive(true); + roleMembers.add(rm); + + Role localRole = new Role().setName("dom2:role.admin").setRoleMembers(roleMembers); + + // get role call for the admin role of service getting added + + Mockito.when(dbsvc.getRole("dom2", "admin", Boolean.FALSE, Boolean.TRUE, Boolean.FALSE)) + .thenReturn(localRole); + + List members = new ArrayList<>(); + members.add("user.joe"); + members.add("dom2.svc1"); + Set recipients = membershipDecisionNotificationCommon.getRecipients(members); + + assertEquals(3, recipients.size()); + assertTrue(recipients.contains("user.joe")); + assertTrue(recipients.contains("user.approver1")); + assertTrue(recipients.contains("user.approver2")); + } + + @Test + public void testGetRecipientsGroupAdmin() { + DBService dbsvc = Mockito.mock(DBService.class); + List roleMembers = new ArrayList<>(); + RoleMember rm = new RoleMember().setMemberName("user.approver1").setActive(true); + roleMembers.add(rm); + + rm = new RoleMember().setMemberName("user.approver2").setActive(true); + roleMembers.add(rm); + + Role localRole = new Role().setName("dom1:role.admin").setRoleMembers(roleMembers); + + // get role call for the admin role of service getting added + Mockito.when(dbsvc.getRole("dom1", "admin", Boolean.FALSE, Boolean.TRUE, Boolean.FALSE)) + .thenReturn(localRole); + Group group = new Group(); + Mockito.when(dbsvc.getGroup("dom1", "group1", Boolean.FALSE, Boolean.FALSE)) + .thenReturn(group); + + DomainRoleMembersFetcher domainRoleMembersFetcher = new DomainRoleMembersFetcher(dbsvc, USER_DOMAIN_PREFIX); + MembershipDecisionNotificationCommon membershipDecisionNotificationCommon = + new MembershipDecisionNotificationCommon(dbsvc, domainRoleMembersFetcher, USER_DOMAIN_PREFIX); + + List members = new ArrayList<>(); + members.add("user.jane"); + members.add("dom1:group.group1"); + Set recipients = membershipDecisionNotificationCommon.getRecipients(members); + + assertEquals(3, recipients.size()); + assertTrue(recipients.contains("user.jane")); + assertTrue(recipients.contains("user.approver1")); + assertTrue(recipients.contains("user.approver2")); + } + + @Test + public void testGetRecipientsGroupNotifyRoles() { + DBService dbsvc = Mockito.mock(DBService.class); + List roleMembers = new ArrayList<>(); + RoleMember rm = new RoleMember().setMemberName("user.notifier1").setActive(true); + roleMembers.add(rm); + + rm = new RoleMember().setMemberName("user.notifier2").setActive(true); + roleMembers.add(rm); + + Role notifyRole1 = new Role().setName("dom2:role.notify1").setRoleMembers(roleMembers); + + roleMembers = new ArrayList<>(); + rm = new RoleMember().setMemberName("user.joe").setActive(true); + roleMembers.add(rm); + + rm = new RoleMember().setMemberName("user.dom").setActive(true); + roleMembers.add(rm); + + Role notifyRole2 = new Role().setName("dom2:role.notify2").setRoleMembers(roleMembers); + + Group group = new Group().setNotifyRoles("dom2:role.notify2,dom2:role.notify1"); + + Mockito.when(dbsvc.getGroup("dom1", "group1", Boolean.FALSE, Boolean.FALSE)) + .thenReturn(group); + Mockito.when(dbsvc.getRole("dom2", "notify1", Boolean.FALSE, Boolean.TRUE, Boolean.FALSE)) + .thenReturn(notifyRole1); + Mockito.when(dbsvc.getRole("dom2", "notify2", Boolean.FALSE, Boolean.TRUE, Boolean.FALSE)) + .thenReturn(notifyRole2); + DomainRoleMembersFetcher domainRoleMembersFetcher = new DomainRoleMembersFetcher(dbsvc, USER_DOMAIN_PREFIX); + MembershipDecisionNotificationCommon membershipDecisionNotificationCommon = + new MembershipDecisionNotificationCommon(dbsvc, domainRoleMembersFetcher, USER_DOMAIN_PREFIX); + + List members = new ArrayList<>(); + members.add("user.jane"); + members.add("dom1:group.group1"); + Set recipients = membershipDecisionNotificationCommon.getRecipients(members); + + assertEquals(5, recipients.size()); + assertTrue(recipients.contains("user.jane")); + assertTrue(recipients.contains("user.notifier1")); + assertTrue(recipients.contains("user.notifier2")); + assertTrue(recipients.contains("user.joe")); + assertTrue(recipients.contains("user.dom")); + } + + @Test + public void testGetRecipientsGroupEmptyAdmin() { + DBService dbsvc = Mockito.mock(DBService.class); + List roleMembers = new ArrayList<>(); + + Role localRole = new Role().setName("dom1:role.admin").setRoleMembers(roleMembers); + + // get role call for the admin role of service getting added + Mockito.when(dbsvc.getRole("dom1", "admin", Boolean.FALSE, Boolean.TRUE, Boolean.FALSE)) + .thenReturn(localRole); + Group group = new Group(); + Mockito.when(dbsvc.getGroup("dom1", "group1", Boolean.FALSE, Boolean.FALSE)) + .thenReturn(group); + + DomainRoleMembersFetcher domainRoleMembersFetcher = new DomainRoleMembersFetcher(dbsvc, USER_DOMAIN_PREFIX); + MembershipDecisionNotificationCommon membershipDecisionNotificationCommon = + new MembershipDecisionNotificationCommon(dbsvc, domainRoleMembersFetcher, USER_DOMAIN_PREFIX); + + List members = new ArrayList<>(); + members.add("user.jane"); + members.add("dom1:group.group1"); + Set recipients = membershipDecisionNotificationCommon.getRecipients(members); + + assertEquals(1, recipients.size()); + assertTrue(recipients.contains("user.jane")); + } +} diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PendingGroupMembershipApprovalNotificationTaskTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PendingGroupMembershipApprovalNotificationTaskTest.java index 1975c083e03..bb3ff043e68 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PendingGroupMembershipApprovalNotificationTaskTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PendingGroupMembershipApprovalNotificationTaskTest.java @@ -31,7 +31,6 @@ import static com.yahoo.athenz.zms.notification.ZMSNotificationManagerTest.getNotificationManager; import static org.mockito.ArgumentMatchers.any; import static org.testng.Assert.*; -import static org.testng.AssertJUnit.assertEquals; public class PendingGroupMembershipApprovalNotificationTaskTest { @Test diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PendingRoleMembershipApprovalNotificationTaskTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PendingRoleMembershipApprovalNotificationTaskTest.java index a02de3e107a..625a612060e 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PendingRoleMembershipApprovalNotificationTaskTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PendingRoleMembershipApprovalNotificationTaskTest.java @@ -31,8 +31,6 @@ import static com.yahoo.athenz.zms.notification.ZMSNotificationManagerTest.getNotificationManager; import static org.mockito.ArgumentMatchers.any; import static org.testng.Assert.*; -import static org.testng.Assert.assertFalse; -import static org.testng.AssertJUnit.assertEquals; public class PendingRoleMembershipApprovalNotificationTaskTest { @Test diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PutGroupMembershipDecisionNotificationTaskTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PutGroupMembershipDecisionNotificationTaskTest.java new file mode 100644 index 00000000000..3a508b25fe9 --- /dev/null +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PutGroupMembershipDecisionNotificationTaskTest.java @@ -0,0 +1,374 @@ +/* + * Copyright The Athenz Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.yahoo.athenz.zms.notification; + +import com.yahoo.athenz.common.server.ServerResourceException; +import com.yahoo.athenz.common.server.notification.*; +import com.yahoo.athenz.zms.DBService; +import com.yahoo.athenz.zms.Role; +import com.yahoo.athenz.zms.RoleMember; +import com.yahoo.rdl.Timestamp; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.yahoo.athenz.common.ServerCommonConsts.USER_DOMAIN_PREFIX; +import static com.yahoo.athenz.common.server.notification.impl.MetricNotificationService.*; +import static com.yahoo.athenz.zms.notification.ZMSNotificationManagerTest.getNotificationManager; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +import static org.testng.Assert.*; +import static org.testng.Assert.assertEquals; + +public class PutGroupMembershipDecisionNotificationTaskTest { + private final NotificationToEmailConverterCommon notificationToEmailConverterCommon = new NotificationToEmailConverterCommon(null); + + @Test + public void testGenerateAndSendPostPutMembershipDecisionNotificationUsers() throws ServerResourceException { + DBService dbsvc = Mockito.mock(DBService.class); + NotificationService mockNotificationService = Mockito.mock(NotificationService.class); + NotificationServiceFactory testfact = Mockito.mock(NotificationServiceFactory.class); + Mockito.when(testfact.create(any())).thenReturn(mockNotificationService); + NotificationManager notificationManager = getNotificationManager(dbsvc, testfact); + notificationManager.shutdown(); + Map details = new HashMap<>(); + details.put("domain", "testdomain1"); + details.put("group", "group1"); + details.put("actionPrincipal", "user.approver1"); + details.put("member", "user.user1"); + details.put("requester", "user.user2"); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Notification.class); + + List notifications = new PutGroupMembershipDecisionNotificationTask(details, true, dbsvc, + USER_DOMAIN_PREFIX, notificationToEmailConverterCommon).getNotifications(); + notificationManager.sendNotifications(notifications); + + Notification notification = new Notification(Notification.Type.GROUP_MEMBER_DECISION); + notification.addRecipient("user.user1") + .addRecipient("user.user2"); + notification.addDetails("domain", "testdomain1").addDetails("group", "group1") + .addDetails("actionPrincipal", "user.approver1").addDetails("member", "user.user1") + .addDetails("requester", "user.user2"); + + PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToEmailConverter converter = + new PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToEmailConverter(notificationToEmailConverterCommon, true); + notification.setNotificationToEmailConverter(converter); + + PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToMetricConverter metricConverter = + new PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToMetricConverter(); + notification.setNotificationToMetricConverter(metricConverter); + + Mockito.verify(mockNotificationService, atLeastOnce()).notify(captor.capture()); + Notification actualNotification = captor.getValue(); + + assertEquals(actualNotification, notification); + } + + @Test + public void testGenerateAndSendPostPutMembershipDecisionNotificationService() throws ServerResourceException { + DBService dbsvc = Mockito.mock(DBService.class); + NotificationService mockNotificationService = Mockito.mock(NotificationService.class); + NotificationServiceFactory testfact = Mockito.mock(NotificationServiceFactory.class); + Mockito.when(testfact.create(any())).thenReturn(mockNotificationService); + NotificationManager notificationManager = getNotificationManager(dbsvc, testfact); + notificationManager.shutdown(); + Map details = new HashMap<>(); + details.put("domain", "testdomain1"); + details.put("group", "group1"); + details.put("actionPrincipal", "user.approver1"); + details.put("member", "dom2.testsvc1"); + + List roleMembers = new ArrayList<>(); + RoleMember rm = new RoleMember().setMemberName("user.approver1").setActive(true); + roleMembers.add(rm); + + rm = new RoleMember().setMemberName("user.approver2").setActive(true); + roleMembers.add(rm); + + Role localRole = new Role().setName("dom2:role.admin").setRoleMembers(roleMembers); + + // get role call for the admin role of service getting added + Mockito.when(dbsvc.getRole("dom2", "admin", Boolean.FALSE, Boolean.TRUE, Boolean.FALSE)) + .thenReturn(localRole); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Notification.class); + + List notifications = new PutGroupMembershipDecisionNotificationTask(details, true, dbsvc, + USER_DOMAIN_PREFIX, notificationToEmailConverterCommon).getNotifications(); + notificationManager.sendNotifications(notifications); + + Notification notification = new Notification(Notification.Type.GROUP_MEMBER_DECISION); + notification.addRecipient("user.approver1") + .addRecipient("user.approver2"); + notification.addDetails("domain", "testdomain1").addDetails("group", "group1") + .addDetails("actionPrincipal", "user.approver1").addDetails("member", "dom2.testsvc1"); + + PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToEmailConverter converter = + new PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToEmailConverter(notificationToEmailConverterCommon, true); + notification.setNotificationToEmailConverter(converter); + + PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToMetricConverter metricConverter = + new PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToMetricConverter(); + notification.setNotificationToMetricConverter(metricConverter); + + Mockito.verify(mockNotificationService, atLeastOnce()).notify(captor.capture()); + Notification actualNotification = captor.getValue(); + + assertEquals(actualNotification, notification); + } + + @Test + public void testGenerateAndSendPostPutMembershipNotificationNullNotificationSvc() throws ServerResourceException { + + DBService dbsvc = Mockito.mock(DBService.class); + NotificationServiceFactory testfact = Mockito.mock(NotificationServiceFactory.class); + Mockito.when(testfact.create(any())).thenReturn(null); + NotificationService mockNotificationService = Mockito.mock(NotificationService.class); + NotificationManager notificationManager = getNotificationManager(dbsvc, testfact); + notificationManager.shutdown(); + List notifications = new PutGroupMembershipDecisionNotificationTask(null, true, dbsvc, + USER_DOMAIN_PREFIX, notificationToEmailConverterCommon).getNotifications(); + notificationManager.sendNotifications(notifications); + verify(mockNotificationService, never()).notify(any(Notification.class)); + } + + @Test + public void testGenerateAndSendPostPutMembershipNotificationNullGroup() throws ServerResourceException { + + DBService dbsvc = Mockito.mock(DBService.class); + NotificationService mockNotificationService = Mockito.mock(NotificationService.class); + NotificationServiceFactory testfact = Mockito.mock(NotificationServiceFactory.class); + Mockito.when(testfact.create(any())).thenReturn(mockNotificationService); + NotificationManager notificationManager = getNotificationManager(dbsvc, testfact); + notificationManager.shutdown(); + Map details = new HashMap<>(); + details.put("domain", "testdomain1"); + details.put("group", "group1"); + details.put("actionPrincipal", "user.approver1"); + details.put("member", "dom2:group.nullgrp"); + + // get role call for the admin role of service getting added + + Mockito.when(dbsvc.getGroup("dom2", "nullgrp", Boolean.FALSE, Boolean.FALSE)) + .thenReturn(null); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Notification.class); + + List notifications = new PutGroupMembershipDecisionNotificationTask(details, true, dbsvc, + USER_DOMAIN_PREFIX, notificationToEmailConverterCommon).getNotifications(); + notificationManager.sendNotifications(notifications); + + Mockito.verify(mockNotificationService, atMost(0)).notify(captor.capture()); + } + + @Test + public void testDescription() { + DBService dbsvc = Mockito.mock(DBService.class); + PutGroupMembershipDecisionNotificationTask putgroupMembershipDecisionNotificationTask = + new PutGroupMembershipDecisionNotificationTask(new HashMap<>(), true, dbsvc, USER_DOMAIN_PREFIX, + notificationToEmailConverterCommon); + + String description = putgroupMembershipDecisionNotificationTask.getDescription(); + assertEquals("Pending Group Membership Decision Notification", description); + } + + @Test + public void testGetRejectEmailBody() { + System.setProperty("athenz.notification_workflow_url", "https://athenz.example.com/workflow"); + System.setProperty("athenz.notification_support_text", "#Athenz slack channel"); + System.setProperty("athenz.notification_support_url", "https://link.to.athenz.channel.com"); + System.setProperty("athenz.notification_athenz_ui_url", "https://athenz.example.com"); + + Map details = new HashMap<>(); + details.put("domain", "dom1"); + details.put("group", "group1"); + details.put("member", "user.member1"); + details.put("reason", "test reason"); + details.put("requester", "user.requester"); + details.put("actionPrincipal", "user.actionPrincipal"); + details.put("membershipDecision", "reject"); + + Notification notification = new Notification(Notification.Type.GROUP_MEMBER_DECISION); + notification.setDetails(details); + PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToEmailConverter converter = + new PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToEmailConverter(new NotificationToEmailConverterCommon(null), false); + NotificationEmail notificationAsEmail = converter.getNotificationAsEmail(notification); + + String body = notificationAsEmail.getBody(); + assertNotNull(body); + assertTrue(body.contains("dom1")); + assertTrue(body.contains("group1")); + assertTrue(body.contains("user.member1")); + assertTrue(body.contains("test reason")); + assertTrue(body.contains("user.requester")); + assertTrue(body.contains("user.actionPrincipal")); + assertTrue(body.contains("https://athenz.example.com")); + assertTrue(body.contains("Pending Group Membership Rejected Details")); + assertTrue(body.contains("REJECTED BY")); + + // Make sure support text and url do not appear + + assertFalse(body.contains("slack")); + assertFalse(body.contains("link.to.athenz.channel.com")); + + System.clearProperty("athenz.notification_workflow_url"); + System.clearProperty("athenz.notification_support_text"); + System.clearProperty("athenz.notification_support_url"); + System.clearProperty("athenz.notification_athenz_ui_url"); + } + + @Test + public void testGetApproveEmailBody() { + System.setProperty("athenz.notification_workflow_url", "https://athenz.example.com/workflow"); + System.setProperty("athenz.notification_support_text", "#Athenz slack channel"); + System.setProperty("athenz.notification_support_url", "https://link.to.athenz.channel.com"); + System.setProperty("athenz.notification_athenz_ui_url", "https://athenz.example.com"); + + Map details = new HashMap<>(); + details.put("domain", "dom1"); + details.put("group", "group1"); + details.put("member", "user.member1"); + details.put("reason", "test reason"); + details.put("requester", "user.requester"); + details.put("actionPrincipal", "user.actionPrincipal"); + details.put("membershipDecision", "reject"); + + Notification notification = new Notification(Notification.Type.GROUP_MEMBER_DECISION); + notification.setDetails(details); + PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToEmailConverter converter = + new PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToEmailConverter(new NotificationToEmailConverterCommon(null), true); + NotificationEmail notificationAsEmail = converter.getNotificationAsEmail(notification); + + String body = notificationAsEmail.getBody(); + assertNotNull(body); + assertTrue(body.contains("dom1")); + assertTrue(body.contains("group1")); + assertTrue(body.contains("user.member1")); + assertTrue(body.contains("test reason")); + assertTrue(body.contains("user.requester")); + assertTrue(body.contains("user.actionPrincipal")); + assertTrue(body.contains("https://athenz.example.com")); + assertTrue(body.contains("Pending Group Membership Approved Details")); + assertTrue(body.contains("APPROVED BY")); + + // Make sure support text and url do not appear + + assertFalse(body.contains("slack")); + assertFalse(body.contains("link.to.athenz.channel.com")); + + System.clearProperty("athenz.notification_workflow_url"); + System.clearProperty("athenz.notification_support_text"); + System.clearProperty("athenz.notification_support_url"); + System.clearProperty("athenz.notification_athenz_ui_url"); + } + + @Test + public void getRejectEmailSubject() { + Notification notification = new Notification(Notification.Type.GROUP_MEMBER_DECISION); + PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToEmailConverter converter = + new PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToEmailConverter(notificationToEmailConverterCommon, false); + NotificationEmail notificationAsEmail = converter.getNotificationAsEmail(notification); + String subject = notificationAsEmail.getSubject(); + assertEquals(subject, "Athenz Pending Group Member Rejected"); + } + + @Test + public void getApproveEmailSubject() { + Notification notification = new Notification(Notification.Type.ROLE_MEMBER_DECISION); + PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToEmailConverter converter = + new PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToEmailConverter(notificationToEmailConverterCommon, true); + NotificationEmail notificationAsEmail = converter.getNotificationAsEmail(notification); + String subject = notificationAsEmail.getSubject(); + assertEquals(subject, "Athenz Pending Group Member Approved"); + } + + @Test + public void testGetApproveNotificationAsMetric() { + Map details = new HashMap<>(); + details.put("domain", "dom1"); + details.put("group", "group1"); + details.put("member", "user.member1"); + details.put("reason", "test reason"); + details.put("requester", "user.requester"); + details.put("actionPrincipal", "user.actionPrincipal"); + details.put("membershipDecision", "approve"); + + Notification notification = new Notification(Notification.Type.GROUP_MEMBER_DECISION); + notification.setDetails(details); + + PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToMetricConverter converter = + new PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToMetricConverter(); + + NotificationMetric notificationAsMetrics = converter.getNotificationAsMetrics(notification, + Timestamp.fromMillis(System.currentTimeMillis())); + String[] record = new String[] { + METRIC_NOTIFICATION_TYPE_KEY, "pending_group_membership_decision", + METRIC_NOTIFICATION_DOMAIN_KEY, "dom1", + METRIC_NOTIFICATION_GROUP_KEY, "group1", + METRIC_NOTIFICATION_MEMBER_KEY, "user.member1", + METRIC_NOTIFICATION_REASON_KEY, "test reason", + METRIC_NOTIFICATION_REQUESTER_KEY, "user.requester", + METRIC_NOTIFICATION_MEMBERSHIP_DECISION, "approve" + }; + + List expectedAttributes = new ArrayList<>(); + expectedAttributes.add(record); + + assertEquals(new NotificationMetric(expectedAttributes), notificationAsMetrics); + } + + @Test + public void testGetRejectNotificationAsMetric() { + Map details = new HashMap<>(); + details.put("domain", "dom1"); + details.put("group", "group1"); + details.put("member", "user.member1"); + details.put("reason", "test reason"); + details.put("requester", "user.requester"); + details.put("actionPrincipal", "user.actionPrincipal"); + details.put("membershipDecision", "reject"); + + Notification notification = new Notification(Notification.Type.GROUP_MEMBER_DECISION); + notification.setDetails(details); + + PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToMetricConverter converter = + new PutGroupMembershipDecisionNotificationTask.PutGroupMembershipDecisionNotificationToMetricConverter(); + + NotificationMetric notificationAsMetrics = converter.getNotificationAsMetrics(notification, + Timestamp.fromMillis(System.currentTimeMillis())); + String[] record = new String[] { + METRIC_NOTIFICATION_TYPE_KEY, "pending_group_membership_decision", + METRIC_NOTIFICATION_DOMAIN_KEY, "dom1", + METRIC_NOTIFICATION_GROUP_KEY, "group1", + METRIC_NOTIFICATION_MEMBER_KEY, "user.member1", + METRIC_NOTIFICATION_REASON_KEY, "test reason", + METRIC_NOTIFICATION_REQUESTER_KEY, "user.requester", + METRIC_NOTIFICATION_MEMBERSHIP_DECISION, "reject" + }; + + List expectedAttributes = new ArrayList<>(); + expectedAttributes.add(record); + + assertEquals(new NotificationMetric(expectedAttributes), notificationAsMetrics); + } +} diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PutGroupMembershipNotificationTaskTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PutGroupMembershipNotificationTaskTest.java index 121c784f064..c7a9ccc8bbe 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PutGroupMembershipNotificationTaskTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PutGroupMembershipNotificationTaskTest.java @@ -33,7 +33,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import static org.testng.Assert.*; -import static org.testng.AssertJUnit.assertEquals; public class PutGroupMembershipNotificationTaskTest { private final NotificationToEmailConverterCommon notificationToEmailConverterCommon = new NotificationToEmailConverterCommon(null); diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PutRoleMembershipDecisionNotificationTaskTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PutRoleMembershipDecisionNotificationTaskTest.java new file mode 100644 index 00000000000..c0f794554ac --- /dev/null +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PutRoleMembershipDecisionNotificationTaskTest.java @@ -0,0 +1,506 @@ +/* + * Copyright The Athenz Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.yahoo.athenz.zms.notification; + +import com.yahoo.athenz.common.server.ServerResourceException; +import com.yahoo.athenz.common.server.notification.*; +import com.yahoo.athenz.zms.DBService; +import com.yahoo.athenz.zms.Group; +import com.yahoo.athenz.zms.Role; +import com.yahoo.athenz.zms.RoleMember; +import com.yahoo.rdl.Timestamp; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.yahoo.athenz.common.ServerCommonConsts.USER_DOMAIN_PREFIX; +import static com.yahoo.athenz.common.server.notification.impl.MetricNotificationService.*; +import static com.yahoo.athenz.zms.notification.ZMSNotificationManagerTest.getNotificationManager; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +import static org.testng.Assert.*; +import static org.testng.Assert.assertEquals; + +public class PutRoleMembershipDecisionNotificationTaskTest { + private final NotificationToEmailConverterCommon notificationToEmailConverterCommon = + new NotificationToEmailConverterCommon(null); + + @Test + public void testGenerateAndSendPostPutMembershipDecisionNotificationGroupAdmin() throws ServerResourceException { + DBService dbsvc = Mockito.mock(DBService.class); + NotificationService mockNotificationService = Mockito.mock(NotificationService.class); + NotificationServiceFactory testfact = Mockito.mock(NotificationServiceFactory.class); + Mockito.when(testfact.create(any())).thenReturn(mockNotificationService); + NotificationManager notificationManager = getNotificationManager(dbsvc, testfact); + notificationManager.shutdown(); + Map details = new HashMap<>(); + details.put("domain", "testdomain1"); + details.put("role", "role1"); + details.put("actionPrincipal", "user.approver1"); + details.put("member", "dom1:group.group1"); + details.put("requester", "user.user2"); + + List roleMembers = new ArrayList<>(); + RoleMember rm = new RoleMember().setMemberName("user.admin1").setActive(true); + roleMembers.add(rm); + + rm = new RoleMember().setMemberName("user.admin2").setActive(true); + roleMembers.add(rm); + + Role adminRole = new Role().setName("dom2:role.admin").setRoleMembers(roleMembers); + + Group group = new Group(); + + Mockito.when(dbsvc.getGroup("dom1", "group1", Boolean.FALSE, Boolean.FALSE)) + .thenReturn(group); + Mockito.when(dbsvc.getRole("dom1", "admin", Boolean.FALSE, Boolean.TRUE, Boolean.FALSE)) + .thenReturn(adminRole); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Notification.class); + + List notifications = new PutRoleMembershipDecisionNotificationTask(details, true, dbsvc, + USER_DOMAIN_PREFIX, notificationToEmailConverterCommon).getNotifications(); + notificationManager.sendNotifications(notifications); + + Notification notification = new Notification(Notification.Type.ROLE_MEMBER_DECISION); + notification.addRecipient("user.admin1") + .addRecipient("user.admin2") + .addRecipient("user.user2"); + notification.addDetails("domain", "testdomain1").addDetails("role", "role1") + .addDetails("actionPrincipal", "user.approver1").addDetails("member", "dom1:group.group1") + .addDetails("requester", "user.user2"); + + PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter converter = + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter(notificationToEmailConverterCommon, true); + notification.setNotificationToEmailConverter(converter); + + PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToMetricConverter metricConverter = + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToMetricConverter(); + notification.setNotificationToMetricConverter(metricConverter); + + Mockito.verify(mockNotificationService, atLeastOnce()).notify(captor.capture()); + Notification actualNotification = captor.getValue(); + + assertEquals(actualNotification, notification); + } + + @Test + public void testGenerateAndSendPostPutMembershipDecisionNotificationGroupNotifyRoles() throws ServerResourceException { + DBService dbsvc = Mockito.mock(DBService.class); + NotificationService mockNotificationService = Mockito.mock(NotificationService.class); + NotificationServiceFactory testfact = Mockito.mock(NotificationServiceFactory.class); + Mockito.when(testfact.create(any())).thenReturn(mockNotificationService); + NotificationManager notificationManager = getNotificationManager(dbsvc, testfact); + notificationManager.shutdown(); + Map details = new HashMap<>(); + details.put("domain", "testdomain1"); + details.put("role", "role1"); + details.put("actionPrincipal", "user.approver1"); + details.put("member", "dom1:group.group1"); + details.put("requester", "user.user2"); + + List roleMembers = new ArrayList<>(); + RoleMember rm = new RoleMember().setMemberName("user.notifier1").setActive(true); + roleMembers.add(rm); + + rm = new RoleMember().setMemberName("user.notifier2").setActive(true); + roleMembers.add(rm); + + Role notifyRole1 = new Role().setName("dom2:role.notify1").setRoleMembers(roleMembers); + + roleMembers = new ArrayList<>(); + rm = new RoleMember().setMemberName("user.joe").setActive(true); + roleMembers.add(rm); + + rm = new RoleMember().setMemberName("user.dom").setActive(true); + roleMembers.add(rm); + + Role notifyRole2 = new Role().setName("dom2:role.notify2").setRoleMembers(roleMembers); + + Group group = new Group().setNotifyRoles("dom2:role.notify2,dom2:role.notify1"); + + Mockito.when(dbsvc.getGroup("dom1", "group1", Boolean.FALSE, Boolean.FALSE)) + .thenReturn(group); + Mockito.when(dbsvc.getRole("dom2", "notify1", Boolean.FALSE, Boolean.TRUE, Boolean.FALSE)) + .thenReturn(notifyRole1); + Mockito.when(dbsvc.getRole("dom2", "notify2", Boolean.FALSE, Boolean.TRUE, Boolean.FALSE)) + .thenReturn(notifyRole2); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Notification.class); + + List notifications = new PutRoleMembershipDecisionNotificationTask(details, true, dbsvc, + USER_DOMAIN_PREFIX, notificationToEmailConverterCommon).getNotifications(); + notificationManager.sendNotifications(notifications); + + Notification notification = new Notification(Notification.Type.ROLE_MEMBER_DECISION); + notification.addRecipient("user.notifier1") + .addRecipient("user.notifier2") + .addRecipient("user.joe") + .addRecipient("user.dom") + .addRecipient("user.user2"); + notification.addDetails("domain", "testdomain1").addDetails("role", "role1") + .addDetails("actionPrincipal", "user.approver1").addDetails("member", "dom1:group.group1") + .addDetails("requester", "user.user2"); + + PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter converter = + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter(notificationToEmailConverterCommon, true); + notification.setNotificationToEmailConverter(converter); + + PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToMetricConverter metricConverter = + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToMetricConverter(); + notification.setNotificationToMetricConverter(metricConverter); + + Mockito.verify(mockNotificationService, atLeastOnce()).notify(captor.capture()); + Notification actualNotification = captor.getValue(); + + assertEquals(actualNotification, notification); + } + + @Test + public void testGenerateAndSendPostPutMembershipDecisionNotificationUsers() throws ServerResourceException { + DBService dbsvc = Mockito.mock(DBService.class); + NotificationService mockNotificationService = Mockito.mock(NotificationService.class); + NotificationServiceFactory testfact = Mockito.mock(NotificationServiceFactory.class); + Mockito.when(testfact.create(any())).thenReturn(mockNotificationService); + NotificationManager notificationManager = getNotificationManager(dbsvc, testfact); + notificationManager.shutdown(); + Map details = new HashMap<>(); + details.put("domain", "testdomain1"); + details.put("role", "role1"); + details.put("actionPrincipal", "user.approver1"); + details.put("member", "user.user1"); + details.put("requester", "user.user2"); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Notification.class); + + List notifications = new PutRoleMembershipDecisionNotificationTask(details, true, dbsvc, + USER_DOMAIN_PREFIX, notificationToEmailConverterCommon).getNotifications(); + notificationManager.sendNotifications(notifications); + + Notification notification = new Notification(Notification.Type.ROLE_MEMBER_DECISION); + notification.addRecipient("user.user1") + .addRecipient("user.user2"); + notification.addDetails("domain", "testdomain1").addDetails("role", "role1") + .addDetails("actionPrincipal", "user.approver1").addDetails("member", "user.user1") + .addDetails("requester", "user.user2"); + + PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter converter = + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter(notificationToEmailConverterCommon, true); + notification.setNotificationToEmailConverter(converter); + + PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToMetricConverter metricConverter = + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToMetricConverter(); + notification.setNotificationToMetricConverter(metricConverter); + + Mockito.verify(mockNotificationService, atLeastOnce()).notify(captor.capture()); + Notification actualNotification = captor.getValue(); + + assertEquals(actualNotification, notification); + } + + @Test + public void testGenerateAndSendPostPutMembershipDecisionNotificationService() throws ServerResourceException { + DBService dbsvc = Mockito.mock(DBService.class); + NotificationService mockNotificationService = Mockito.mock(NotificationService.class); + NotificationServiceFactory testfact = Mockito.mock(NotificationServiceFactory.class); + Mockito.when(testfact.create(any())).thenReturn(mockNotificationService); + NotificationManager notificationManager = getNotificationManager(dbsvc, testfact); + notificationManager.shutdown(); + Map details = new HashMap<>(); + details.put("domain", "testdomain1"); + details.put("role", "role1"); + details.put("actionPrincipal", "user.approver1"); + details.put("member", "dom2.testsvc1"); + + List roleMembers = new ArrayList<>(); + RoleMember rm = new RoleMember().setMemberName("user.approver1").setActive(true); + roleMembers.add(rm); + + rm = new RoleMember().setMemberName("user.approver2").setActive(true); + roleMembers.add(rm); + + Role localRole = new Role().setName("dom2:role.admin").setRoleMembers(roleMembers); + + // get role call for the admin role of service getting added + Mockito.when(dbsvc.getRole("dom2", "admin", Boolean.FALSE, Boolean.TRUE, Boolean.FALSE)) + .thenReturn(localRole); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Notification.class); + + List notifications = new PutRoleMembershipDecisionNotificationTask(details, true, dbsvc, + USER_DOMAIN_PREFIX, notificationToEmailConverterCommon).getNotifications(); + notificationManager.sendNotifications(notifications); + + Notification notification = new Notification(Notification.Type.ROLE_MEMBER_DECISION); + notification.addRecipient("user.approver1") + .addRecipient("user.approver2"); + notification.addDetails("domain", "testdomain1").addDetails("role", "role1") + .addDetails("actionPrincipal", "user.approver1").addDetails("member", "dom2.testsvc1"); + + PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter converter = + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter(notificationToEmailConverterCommon, true); + notification.setNotificationToEmailConverter(converter); + + PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToMetricConverter metricConverter = + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToMetricConverter(); + notification.setNotificationToMetricConverter(metricConverter); + + Mockito.verify(mockNotificationService, atLeastOnce()).notify(captor.capture()); + Notification actualNotification = captor.getValue(); + + assertEquals(actualNotification, notification); + } + + @Test + public void testGenerateAndSendPostPutMembershipNotificationNullNotificationSvc() throws ServerResourceException { + + DBService dbsvc = Mockito.mock(DBService.class); + NotificationServiceFactory testfact = Mockito.mock(NotificationServiceFactory.class); + Mockito.when(testfact.create(any())).thenReturn(null); + NotificationService mockNotificationService = Mockito.mock(NotificationService.class); + NotificationManager notificationManager = getNotificationManager(dbsvc, testfact); + notificationManager.shutdown(); + List notifications = new PutRoleMembershipDecisionNotificationTask(null, true, dbsvc, + USER_DOMAIN_PREFIX, notificationToEmailConverterCommon).getNotifications(); + notificationManager.sendNotifications(notifications); + verify(mockNotificationService, never()).notify(any(Notification.class)); + } + + @Test + public void testGenerateAndSendPostPutMembershipNotificationNullGroup() throws ServerResourceException { + + DBService dbsvc = Mockito.mock(DBService.class); + NotificationService mockNotificationService = Mockito.mock(NotificationService.class); + NotificationServiceFactory testfact = Mockito.mock(NotificationServiceFactory.class); + Mockito.when(testfact.create(any())).thenReturn(mockNotificationService); + NotificationManager notificationManager = getNotificationManager(dbsvc, testfact); + notificationManager.shutdown(); + Map details = new HashMap<>(); + details.put("domain", "testdomain1"); + details.put("role", "role1"); + details.put("actionPrincipal", "user.approver1"); + details.put("member", "dom2:group.nullgrp"); + + // get role call for the admin role of service getting added + Mockito.when(dbsvc.getGroup("dom2", "nullgrp", Boolean.FALSE, Boolean.FALSE)) + .thenReturn(null); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Notification.class); + + List notifications = new PutRoleMembershipDecisionNotificationTask(details, true, dbsvc, + USER_DOMAIN_PREFIX, notificationToEmailConverterCommon).getNotifications(); + notificationManager.sendNotifications(notifications); + + Mockito.verify(mockNotificationService, atMost(0)).notify(captor.capture()); + } + + @Test + public void testDescription() { + DBService dbsvc = Mockito.mock(DBService.class); + PutRoleMembershipDecisionNotificationTask putRoleMembershipDecisionNotificationTask = + new PutRoleMembershipDecisionNotificationTask(new HashMap<>(), true, dbsvc, USER_DOMAIN_PREFIX, + notificationToEmailConverterCommon); + + String description = putRoleMembershipDecisionNotificationTask.getDescription(); + assertEquals("Pending Membership Decision Notification", description); + } + + @Test + public void testGetRejectEmailBody() { + System.setProperty("athenz.notification_workflow_url", "https://athenz.example.com/workflow"); + System.setProperty("athenz.notification_support_text", "#Athenz slack channel"); + System.setProperty("athenz.notification_support_url", "https://link.to.athenz.channel.com"); + System.setProperty("athenz.notification_athenz_ui_url", "https://athenz.example.com"); + + Map details = new HashMap<>(); + details.put("domain", "dom1"); + details.put("role", "role1"); + details.put("member", "user.member1"); + details.put("reason", "test reason"); + details.put("requester", "user.requester"); + details.put("actionPrincipal", "user.actionPrincipal"); + details.put("membershipDecision", "reject"); + + Notification notification = new Notification(Notification.Type.ROLE_MEMBER_DECISION); + notification.setDetails(details); + PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter converter = + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter(new NotificationToEmailConverterCommon(null), false); + NotificationEmail notificationAsEmail = converter.getNotificationAsEmail(notification); + + String body = notificationAsEmail.getBody(); + assertNotNull(body); + assertTrue(body.contains("dom1")); + assertTrue(body.contains("role1")); + assertTrue(body.contains("user.member1")); + assertTrue(body.contains("test reason")); + assertTrue(body.contains("user.requester")); + assertTrue(body.contains("user.actionPrincipal")); + assertTrue(body.contains("https://athenz.example.com")); + assertTrue(body.contains("Pending Membership Rejected Details")); + assertTrue(body.contains("REJECTED BY")); + + // Make sure support text and url do not appear + + assertFalse(body.contains("slack")); + assertFalse(body.contains("link.to.athenz.channel.com")); + + System.clearProperty("athenz.notification_workflow_url"); + System.clearProperty("athenz.notification_support_text"); + System.clearProperty("athenz.notification_support_url"); + System.clearProperty("athenz.notification_athenz_ui_url"); + } + + @Test + public void testGetApproveEmailBody() { + System.setProperty("athenz.notification_workflow_url", "https://athenz.example.com/workflow"); + System.setProperty("athenz.notification_support_text", "#Athenz slack channel"); + System.setProperty("athenz.notification_support_url", "https://link.to.athenz.channel.com"); + System.setProperty("athenz.notification_athenz_ui_url", "https://athenz.example.com"); + + Map details = new HashMap<>(); + details.put("domain", "dom1"); + details.put("role", "role1"); + details.put("member", "user.member1"); + details.put("reason", "test reason"); + details.put("requester", "user.requester"); + details.put("actionPrincipal", "user.actionPrincipal"); + details.put("membershipDecision", "reject"); + + Notification notification = new Notification(Notification.Type.ROLE_MEMBER_DECISION); + notification.setDetails(details); + PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter converter = + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter(new NotificationToEmailConverterCommon(null), true); + NotificationEmail notificationAsEmail = converter.getNotificationAsEmail(notification); + + String body = notificationAsEmail.getBody(); + assertNotNull(body); + assertTrue(body.contains("dom1")); + assertTrue(body.contains("role1")); + assertTrue(body.contains("user.member1")); + assertTrue(body.contains("test reason")); + assertTrue(body.contains("user.requester")); + assertTrue(body.contains("user.actionPrincipal")); + assertTrue(body.contains("https://athenz.example.com")); + assertTrue(body.contains("Pending Membership Approved Details")); + assertTrue(body.contains("APPROVED BY")); + + // Make sure support text and url do not appear + + assertFalse(body.contains("slack")); + assertFalse(body.contains("link.to.athenz.channel.com")); + + System.clearProperty("athenz.notification_workflow_url"); + System.clearProperty("athenz.notification_support_text"); + System.clearProperty("athenz.notification_support_url"); + System.clearProperty("athenz.notification_athenz_ui_url"); + } + + @Test + public void getRejectEmailSubject() { + Notification notification = new Notification(Notification.Type.ROLE_MEMBER_DECISION); + PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter converter = + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter(notificationToEmailConverterCommon, false); + NotificationEmail notificationAsEmail = converter.getNotificationAsEmail(notification); + String subject = notificationAsEmail.getSubject(); + assertEquals(subject, "Athenz Pending Role Member Rejected"); + } + + @Test + public void getApproveEmailSubject() { + Notification notification = new Notification(Notification.Type.ROLE_MEMBER_DECISION); + PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter converter = + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToEmailConverter(notificationToEmailConverterCommon, true); + NotificationEmail notificationAsEmail = converter.getNotificationAsEmail(notification); + String subject = notificationAsEmail.getSubject(); + assertEquals(subject, "Athenz Pending Role Member Approved"); + } + + @Test + public void testGetApproveNotificationAsMetric() { + Map details = new HashMap<>(); + details.put("domain", "dom1"); + details.put("role", "role1"); + details.put("member", "user.member1"); + details.put("reason", "test reason"); + details.put("requester", "user.requester"); + details.put("actionPrincipal", "user.actionPrincipal"); + details.put("membershipDecision", "approve"); + + Notification notification = new Notification(Notification.Type.ROLE_MEMBER_DECISION); + notification.setDetails(details); + + PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToMetricConverter converter = + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToMetricConverter(); + + NotificationMetric notificationAsMetrics = converter.getNotificationAsMetrics(notification, + Timestamp.fromMillis(System.currentTimeMillis())); + String[] record = new String[] { + METRIC_NOTIFICATION_TYPE_KEY, "pending_role_membership_decision", + METRIC_NOTIFICATION_DOMAIN_KEY, "dom1", + METRIC_NOTIFICATION_ROLE_KEY, "role1", + METRIC_NOTIFICATION_MEMBER_KEY, "user.member1", + METRIC_NOTIFICATION_REASON_KEY, "test reason", + METRIC_NOTIFICATION_REQUESTER_KEY, "user.requester", + METRIC_NOTIFICATION_MEMBERSHIP_DECISION, "approve" + }; + + List expectedAttributes = new ArrayList<>(); + expectedAttributes.add(record); + + assertEquals(new NotificationMetric(expectedAttributes), notificationAsMetrics); + } + + @Test + public void testGetRejectNotificationAsMetric() { + Map details = new HashMap<>(); + details.put("domain", "dom1"); + details.put("role", "role1"); + details.put("member", "user.member1"); + details.put("reason", "test reason"); + details.put("requester", "user.requester"); + details.put("actionPrincipal", "user.actionPrincipal"); + details.put("membershipDecision", "reject"); + + Notification notification = new Notification(Notification.Type.ROLE_MEMBER_DECISION); + notification.setDetails(details); + + PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToMetricConverter converter = + new PutRoleMembershipDecisionNotificationTask.PutRoleMembershipDecisionNotificationToMetricConverter(); + + NotificationMetric notificationAsMetrics = converter.getNotificationAsMetrics(notification, + Timestamp.fromMillis(System.currentTimeMillis())); + String[] record = new String[] { + METRIC_NOTIFICATION_TYPE_KEY, "pending_role_membership_decision", + METRIC_NOTIFICATION_DOMAIN_KEY, "dom1", + METRIC_NOTIFICATION_ROLE_KEY, "role1", + METRIC_NOTIFICATION_MEMBER_KEY, "user.member1", + METRIC_NOTIFICATION_REASON_KEY, "test reason", + METRIC_NOTIFICATION_REQUESTER_KEY, "user.requester", + METRIC_NOTIFICATION_MEMBERSHIP_DECISION, "reject" + }; + + List expectedAttributes = new ArrayList<>(); + expectedAttributes.add(record); + + assertEquals(new NotificationMetric(expectedAttributes), notificationAsMetrics); + } +} diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PutRoleMembershipNotificationTaskTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PutRoleMembershipNotificationTaskTest.java index 7e3b89e9fbf..ab5e863be15 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PutRoleMembershipNotificationTaskTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/PutRoleMembershipNotificationTaskTest.java @@ -36,8 +36,6 @@ import static org.mockito.Mockito.*; import static org.mockito.Mockito.never; import static org.testng.Assert.*; -import static org.testng.Assert.assertFalse; -import static org.testng.AssertJUnit.assertEquals; public class PutRoleMembershipNotificationTaskTest { private final NotificationToEmailConverterCommon notificationToEmailConverterCommon = new NotificationToEmailConverterCommon(null); diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/RoleMemberExpiryNotificationTaskTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/RoleMemberExpiryNotificationTaskTest.java index f3b09fc470d..79f46ebc4c7 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/RoleMemberExpiryNotificationTaskTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/RoleMemberExpiryNotificationTaskTest.java @@ -34,7 +34,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.testng.Assert.*; -import static org.testng.AssertJUnit.assertEquals; public class RoleMemberExpiryNotificationTaskTest { final NotificationToEmailConverterCommon notificationToEmailConverterCommon = new NotificationToEmailConverterCommon(null); diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/RoleMemberNotificationCommonTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/RoleMemberNotificationCommonTest.java index 584fc870825..77efed0e5bd 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/RoleMemberNotificationCommonTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/RoleMemberNotificationCommonTest.java @@ -34,7 +34,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.testng.Assert.*; -import static org.testng.AssertJUnit.assertEquals; public class RoleMemberNotificationCommonTest { diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/RoleMemberReviewNotificationTaskTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/RoleMemberReviewNotificationTaskTest.java index e708a5f11ef..69f8c224edc 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/RoleMemberReviewNotificationTaskTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/notification/RoleMemberReviewNotificationTaskTest.java @@ -34,8 +34,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.testng.Assert.*; -import static org.testng.Assert.assertTrue; -import static org.testng.AssertJUnit.assertEquals; public class RoleMemberReviewNotificationTaskTest { final NotificationToEmailConverterCommon notificationToEmailConverterCommon = new NotificationToEmailConverterCommon(null); diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/provider/ServiceProviderClientTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/provider/ServiceProviderClientTest.java index c30aafaabda..9c34c4c5563 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/provider/ServiceProviderClientTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/provider/ServiceProviderClientTest.java @@ -42,8 +42,8 @@ import static com.yahoo.athenz.zms.ZMSConsts.*; import static com.yahoo.athenz.zms.ZMSConsts.ZMS_PROP_PROVIDER_TRUST_STORE_PASSWORD; import static org.mockito.ArgumentMatchers.any; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNull; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; public class ServiceProviderClientTest { diff --git a/servers/zts/src/test/java/com/yahoo/athenz/zts/cert/impl/FileCertRecordStoreTest.java b/servers/zts/src/test/java/com/yahoo/athenz/zts/cert/impl/FileCertRecordStoreTest.java index 7d014cb2607..d54ac070596 100644 --- a/servers/zts/src/test/java/com/yahoo/athenz/zts/cert/impl/FileCertRecordStoreTest.java +++ b/servers/zts/src/test/java/com/yahoo/athenz/zts/cert/impl/FileCertRecordStoreTest.java @@ -25,8 +25,6 @@ import org.testng.annotations.Test; import static org.testng.Assert.*; -import static org.testng.AssertJUnit.assertFalse; -import static org.testng.AssertJUnit.assertTrue; public class FileCertRecordStoreTest { diff --git a/servers/zts/src/test/java/com/yahoo/athenz/zts/notification/CertFailedRefreshNotificationTaskTest.java b/servers/zts/src/test/java/com/yahoo/athenz/zts/notification/CertFailedRefreshNotificationTaskTest.java index c50ab9b23ae..472a5478c5d 100644 --- a/servers/zts/src/test/java/com/yahoo/athenz/zts/notification/CertFailedRefreshNotificationTaskTest.java +++ b/servers/zts/src/test/java/com/yahoo/athenz/zts/notification/CertFailedRefreshNotificationTaskTest.java @@ -42,8 +42,6 @@ import static com.yahoo.athenz.zts.ZTSConsts.ZTS_PROP_NOTIFICATION_CERT_FAIL_PROVIDER_LIST; import static org.mockito.ArgumentMatchers.*; import static org.testng.Assert.*; -import static org.testng.Assert.assertFalse; -import static org.testng.AssertJUnit.assertEquals; public class CertFailedRefreshNotificationTaskTest { private InstanceCertManager instanceCertManager; diff --git a/servers/zts/src/test/java/com/yahoo/athenz/zts/notification/DomainRoleMembersFetcherTest.java b/servers/zts/src/test/java/com/yahoo/athenz/zts/notification/DomainRoleMembersFetcherTest.java index d2fce63313d..e1773e39853 100644 --- a/servers/zts/src/test/java/com/yahoo/athenz/zts/notification/DomainRoleMembersFetcherTest.java +++ b/servers/zts/src/test/java/com/yahoo/athenz/zts/notification/DomainRoleMembersFetcherTest.java @@ -25,8 +25,8 @@ import static com.yahoo.athenz.common.ServerCommonConsts.USER_DOMAIN_PREFIX; import static org.mockito.Mockito.mock; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertTrue; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; public class DomainRoleMembersFetcherTest { @Test diff --git a/servers/zts/src/test/java/com/yahoo/athenz/zts/token/AccessTokenRequestTest.java b/servers/zts/src/test/java/com/yahoo/athenz/zts/token/AccessTokenRequestTest.java index 4bd314ae015..00c851844e3 100644 --- a/servers/zts/src/test/java/com/yahoo/athenz/zts/token/AccessTokenRequestTest.java +++ b/servers/zts/src/test/java/com/yahoo/athenz/zts/token/AccessTokenRequestTest.java @@ -20,7 +20,6 @@ import org.testng.annotations.Test; import static org.testng.Assert.*; -import static org.testng.AssertJUnit.assertEquals; public class AccessTokenRequestTest { diff --git a/servers/zts/src/test/java/com/yahoo/athenz/zts/token/IdTokenRequestTest.java b/servers/zts/src/test/java/com/yahoo/athenz/zts/token/IdTokenRequestTest.java index a205b15f9d4..88f115ebbab 100644 --- a/servers/zts/src/test/java/com/yahoo/athenz/zts/token/IdTokenRequestTest.java +++ b/servers/zts/src/test/java/com/yahoo/athenz/zts/token/IdTokenRequestTest.java @@ -19,7 +19,6 @@ import org.testng.annotations.Test; import static org.testng.Assert.*; -import static org.testng.AssertJUnit.assertEquals; public class IdTokenRequestTest { diff --git a/servers/zts/src/test/java/com/yahoo/athenz/zts/workload/impl/FileWorkloadRecordStoreTest.java b/servers/zts/src/test/java/com/yahoo/athenz/zts/workload/impl/FileWorkloadRecordStoreTest.java index af7094e2a60..caadfec610d 100644 --- a/servers/zts/src/test/java/com/yahoo/athenz/zts/workload/impl/FileWorkloadRecordStoreTest.java +++ b/servers/zts/src/test/java/com/yahoo/athenz/zts/workload/impl/FileWorkloadRecordStoreTest.java @@ -23,7 +23,6 @@ import java.io.IOException; import static org.testng.Assert.*; -import static org.testng.AssertJUnit.assertTrue; public class FileWorkloadRecordStoreTest { @Test diff --git a/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/AuthHistorySyncerTest.java b/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/AuthHistorySyncerTest.java index 72e6bfd6fec..3f5bf4d1ad8 100644 --- a/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/AuthHistorySyncerTest.java +++ b/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/AuthHistorySyncerTest.java @@ -22,7 +22,7 @@ import com.yahoo.athenz.syncer_common.SyncTimeRange; import org.testng.annotations.Test; -import static org.testng.AssertJUnit.*; +import static org.testng.Assert.*; public class AuthHistorySyncerTest { diff --git a/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/AwsAuthHistoryFetcherTest.java b/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/AwsAuthHistoryFetcherTest.java index 6a5fbf25666..af3818c8430 100644 --- a/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/AwsAuthHistoryFetcherTest.java +++ b/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/AwsAuthHistoryFetcherTest.java @@ -32,8 +32,8 @@ import java.util.Set; import java.util.concurrent.TimeUnit; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertTrue; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; public class AwsAuthHistoryFetcherTest { diff --git a/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/LogsParserUtilsTest.java b/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/LogsParserUtilsTest.java index 6da473a987d..15b13bac184 100644 --- a/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/LogsParserUtilsTest.java +++ b/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/LogsParserUtilsTest.java @@ -24,7 +24,7 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; -import static org.testng.AssertJUnit.fail; +import static org.testng.Assert.fail; public class LogsParserUtilsTest { diff --git a/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/impl/DynamoDBAuthHistorySenderFactoryTest.java b/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/impl/DynamoDBAuthHistorySenderFactoryTest.java index 0c57634905d..f27a0ffbb99 100644 --- a/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/impl/DynamoDBAuthHistorySenderFactoryTest.java +++ b/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/impl/DynamoDBAuthHistorySenderFactoryTest.java @@ -25,7 +25,7 @@ import org.testng.annotations.Test; import static com.yahoo.athenz.syncer.auth.history.AuthHistorySyncerConsts.*; -import static org.testng.AssertJUnit.*; +import static org.testng.Assert.*; public class DynamoDBAuthHistorySenderFactoryTest { diff --git a/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/impl/DynamoDBAuthHistorySenderTest.java b/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/impl/DynamoDBAuthHistorySenderTest.java index 1fa9fb84274..eb32065b048 100644 --- a/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/impl/DynamoDBAuthHistorySenderTest.java +++ b/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/impl/DynamoDBAuthHistorySenderTest.java @@ -40,7 +40,7 @@ import java.util.stream.Collectors; import static com.yahoo.athenz.syncer.auth.history.impl.DynamoDBAuthHistorySender.*; -import static org.testng.AssertJUnit.*; +import static org.testng.Assert.*; public class DynamoDBAuthHistorySenderTest { diff --git a/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/impl/LocalAuthHistoryFetcherTest.java b/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/impl/LocalAuthHistoryFetcherTest.java index b0457d8c5ab..f03b8a62789 100644 --- a/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/impl/LocalAuthHistoryFetcherTest.java +++ b/syncers/auth_history_syncer/src/test/java/com/yahoo/athenz/syncer/auth/history/impl/LocalAuthHistoryFetcherTest.java @@ -27,7 +27,7 @@ import java.util.Set; import java.util.stream.Collectors; -import static org.testng.AssertJUnit.*; +import static org.testng.Assert.*; public class LocalAuthHistoryFetcherTest { diff --git a/ui/package-lock.json b/ui/package-lock.json index 7fd949e23be..579635dc0bb 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -84,7 +84,7 @@ "@wdio/spec-reporter": "8.31.0", "babel-jest": "29.7.0", "babel-preset-current-node-syntax": "1.0.1", - "chromedriver": "120", + "chromedriver": "129", "dotenv": "16.0.2", "expect": "29.0.3", "expect-webdriverio": "4.11.2", @@ -4361,17 +4361,17 @@ } }, "node_modules/chromedriver": { - "version": "120.0.2", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-120.0.2.tgz", - "integrity": "sha512-A6/feXNWoKzkoUISJs/JAi0iIa+t6vwUnQCgGGFK7L/1r7CrreaRynVgnIliQJRdwY/8F41r6Mt3WDD9QXdV8A==", + "version": "129.0.1", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-129.0.1.tgz", + "integrity": "sha512-thJqK3c7p9rIhmjBvs/cgaK0Hk30g7LbnmMXQ2aLnn75ZOiEl/2GBcgc6fw+4GIw1SmOYhnNmaEI1iTP3qob0w==", "dev": true, "hasInstallScript": true, "dependencies": { "@testim/chrome-version": "^1.1.4", - "axios": "^1.6.5", + "axios": "^1.7.4", "compare-versions": "^6.1.0", "extract-zip": "^2.0.1", - "https-proxy-agent": "^5.0.1", + "proxy-agent": "^6.4.0", "proxy-from-env": "^1.1.0", "tcp-port-used": "^1.0.2" }, @@ -4383,9 +4383,9 @@ } }, "node_modules/chromedriver/node_modules/axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "dev": true, "dependencies": { "follow-redirects": "^1.15.6", @@ -4393,6 +4393,47 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/chromedriver/node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/chromedriver/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/chromedriver/node_modules/proxy-agent": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", + "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/chromium-bidi": { "version": "0.4.16", "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", @@ -20036,30 +20077,62 @@ } }, "chromedriver": { - "version": "120.0.2", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-120.0.2.tgz", - "integrity": "sha512-A6/feXNWoKzkoUISJs/JAi0iIa+t6vwUnQCgGGFK7L/1r7CrreaRynVgnIliQJRdwY/8F41r6Mt3WDD9QXdV8A==", + "version": "129.0.1", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-129.0.1.tgz", + "integrity": "sha512-thJqK3c7p9rIhmjBvs/cgaK0Hk30g7LbnmMXQ2aLnn75ZOiEl/2GBcgc6fw+4GIw1SmOYhnNmaEI1iTP3qob0w==", "dev": true, "requires": { "@testim/chrome-version": "^1.1.4", - "axios": "^1.6.5", + "axios": "^1.7.4", "compare-versions": "^6.1.0", "extract-zip": "^2.0.1", - "https-proxy-agent": "^5.0.1", + "proxy-agent": "^6.4.0", "proxy-from-env": "^1.1.0", "tcp-port-used": "^1.0.2" }, "dependencies": { "axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "dev": true, "requires": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } + }, + "https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + }, + "lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true + }, + "proxy-agent": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", + "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "dev": true, + "requires": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + } } } }, diff --git a/ui/package.json b/ui/package.json index cfb8d342e73..1cdbed30c7a 100644 --- a/ui/package.json +++ b/ui/package.json @@ -120,7 +120,7 @@ "@wdio/spec-reporter": "8.31.0", "babel-jest": "29.7.0", "babel-preset-current-node-syntax": "1.0.1", - "chromedriver": "120", + "chromedriver": "129", "dotenv": "16.0.2", "expect": "29.0.3", "expect-webdriverio": "4.11.2", diff --git a/ui/src/__tests__/components/group/__snapshots__/GroupRow.test.js.snap b/ui/src/__tests__/components/group/__snapshots__/GroupRow.test.js.snap index 762a6e177cf..2ba8a64a5bd 100644 --- a/ui/src/__tests__/components/group/__snapshots__/GroupRow.test.js.snap +++ b/ui/src/__tests__/components/group/__snapshots__/GroupRow.test.js.snap @@ -197,7 +197,7 @@ exports[`GroupRow should render 1`] = ` class="emotion-12" data-testid="icon" height="1.25em" - id="" + id="group-history-icon-testui" viewBox="0 0 1024 1024" width="1.25em" > diff --git a/ui/src/__tests__/components/group/__snapshots__/GroupTable.test.js.snap b/ui/src/__tests__/components/group/__snapshots__/GroupTable.test.js.snap index 383a32a1f52..10cc8354abb 100644 --- a/ui/src/__tests__/components/group/__snapshots__/GroupTable.test.js.snap +++ b/ui/src/__tests__/components/group/__snapshots__/GroupTable.test.js.snap @@ -330,7 +330,7 @@ exports[`GroupTable should render 1`] = ` class="emotion-34" data-testid="icon" height="1.25em" - id="" + id="group-history-icon-a" viewBox="0 0 1024 1024" width="1.25em" > @@ -532,7 +532,7 @@ exports[`GroupTable should render 1`] = ` class="emotion-34" data-testid="icon" height="1.25em" - id="" + id="group-history-icon-b" viewBox="0 0 1024 1024" width="1.25em" > diff --git a/ui/src/__tests__/components/role/__snapshots__/RoleRow.test.js.snap b/ui/src/__tests__/components/role/__snapshots__/RoleRow.test.js.snap index 6f53681b80f..ad2d6465c89 100644 --- a/ui/src/__tests__/components/role/__snapshots__/RoleRow.test.js.snap +++ b/ui/src/__tests__/components/role/__snapshots__/RoleRow.test.js.snap @@ -185,7 +185,7 @@ exports[`RoleRow should render 1`] = ` class="emotion-12" data-testid="icon" height="1.25em" - id="" + id="ztssia_cert_rotate-setting-role-button" viewBox="0 0 1024 1024" width="1.25em" > @@ -210,7 +210,7 @@ exports[`RoleRow should render 1`] = ` class="emotion-12" data-testid="icon" height="1.25em" - id="" + id="ztssia_cert_rotate-history-role-button" viewBox="0 0 1024 1024" width="1.25em" > diff --git a/ui/src/__tests__/components/role/__snapshots__/RoleTable.test.js.snap b/ui/src/__tests__/components/role/__snapshots__/RoleTable.test.js.snap index ae00fd9e4c5..cff3cb71a5d 100644 --- a/ui/src/__tests__/components/role/__snapshots__/RoleTable.test.js.snap +++ b/ui/src/__tests__/components/role/__snapshots__/RoleTable.test.js.snap @@ -265,7 +265,7 @@ exports[`RoleTable should render 1`] = ` class="emotion-36" data-testid="icon" height="1.25em" - id="" + id="a-setting-role-button" viewBox="0 0 1024 1024" width="1.25em" > @@ -289,7 +289,7 @@ exports[`RoleTable should render 1`] = ` class="emotion-36" data-testid="icon" height="1.25em" - id="" + id="a-history-role-button" viewBox="0 0 1024 1024" width="1.25em" > @@ -458,7 +458,7 @@ exports[`RoleTable should render 1`] = ` class="emotion-36" data-testid="icon" height="1.25em" - id="" + id="b-setting-role-button" viewBox="0 0 1024 1024" width="1.25em" > @@ -482,7 +482,7 @@ exports[`RoleTable should render 1`] = ` class="emotion-36" data-testid="icon" height="1.25em" - id="" + id="b-history-role-button" viewBox="0 0 1024 1024" width="1.25em" > diff --git a/ui/src/__tests__/redux/reducers/groups.test.js b/ui/src/__tests__/redux/reducers/groups.test.js index 78009155978..33a223df542 100644 --- a/ui/src/__tests__/redux/reducers/groups.test.js +++ b/ui/src/__tests__/redux/reducers/groups.test.js @@ -91,7 +91,7 @@ describe('Groups Reducer', () => { }); it('should load group into the store', () => { const initialState = { - groups: {}, + // not creating groups field here - expecting it to be created by the tested function domainName: domainName, expiry: expiry, }; diff --git a/ui/src/__tests__/redux/reducers/roles.test.js b/ui/src/__tests__/redux/reducers/roles.test.js index a3a0460f9cf..60f9c47d0e1 100644 --- a/ui/src/__tests__/redux/reducers/roles.test.js +++ b/ui/src/__tests__/redux/reducers/roles.test.js @@ -102,6 +102,7 @@ describe('Roles Reducer', () => { }; const expectedState = AppUtils.deepClone(initialState); expectedState.roles['singlerole'] = AppUtils.deepClone(singleStoreRole); + delete initialState.roles; // if initial state doesn't have roles field, it should be created const newState = roles(initialState, action); expect(_.isEqual(newState, expectedState)).toBeTruthy(); }); diff --git a/ui/src/__tests__/spec/tests/domain.spec.js b/ui/src/__tests__/spec/tests/domain.spec.js index f08e4c9a990..c770be6962a 100644 --- a/ui/src/__tests__/spec/tests/domain.spec.js +++ b/ui/src/__tests__/spec/tests/domain.spec.js @@ -29,12 +29,12 @@ describe('Domain', () => { await browser.waitUntil(async () => await pocAnchor.isClickable()); await pocAnchor.click(); let userInput = await $('input[name="poc-name"]'); - await userInput.addValue('jtsang01'); - let userOption = await $('div*=Jimmy Tsang [user.jtsang01]'); + await userInput.addValue('craman'); + let userOption = await $('div*=Chandu Raman [user.craman]'); await userOption.click(); let submitButton = await $('button*=Submit'); await submitButton.click(); - await expect(pocAnchor).toHaveTextContaining('Jimmy Tsang'); + await expect(pocAnchor).toHaveTextContaining('Chandu Raman'); // test adding security poc let securityPocAnchor = await $('a[data-testid="security-poc-link"]'); @@ -43,11 +43,11 @@ describe('Domain', () => { ); await securityPocAnchor.click(); userInput = await $('input[name="poc-name"]'); - await userInput.addValue('jtsang01'); - userOption = await $('div*=Jimmy Tsang [user.jtsang01]'); + await userInput.addValue('craman'); + userOption = await $('div*=Chandu Raman [user.craman]'); await userOption.click(); submitButton = await $('button*=Submit'); await submitButton.click(); - await expect(securityPocAnchor).toHaveTextContaining('Jimmy Tsang'); + await expect(securityPocAnchor).toHaveTextContaining('Chandu Raman'); }); }); diff --git a/ui/src/__tests__/spec/tests/groups.spec.js b/ui/src/__tests__/spec/tests/groups.spec.js new file mode 100644 index 00000000000..cb22312c4ac --- /dev/null +++ b/ui/src/__tests__/spec/tests/groups.spec.js @@ -0,0 +1,90 @@ +/* + * Copyright The Athenz Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +describe('group screen tests', () => { + it('group history should be visible when navigating to it and after page refresh', async () => { + // open browser + await browser.newUser(); + await browser.url(`/`); + // select domain + let domain = 'athenz.dev.functional-test'; + let testDomain = await $(`a*=${domain}`); + await testDomain.click(); + + // ADD test group + // navigate to groups page + let groups = await $('div*=Groups'); + await groups.click(); + // open Add Group screen + let addGroupButton = await $('button*=Add Group'); + await addGroupButton.click(); + // add group info + let inputGroupName = await $('#group-name-input'); + let groupName = 'history-test-group'; + await inputGroupName.addValue(groupName); + // add user + let addMemberInput = await $('[name="member-name"]'); //TODO rename the field + await addMemberInput.addValue('unix.yahoo'); + let userOption = await $('div*=unix.yahoo'); + await userOption.click(); + // submit role + let buttonSubmit = await $('button*=Submit'); + await buttonSubmit.click(); + + // Verify history entry of added group member is present + // open history + let historySvg = await $('.//*[local-name()="svg" and @id="group-history-icon-history-test-group"]'); + await historySvg.click(); + // find row with 'ADD' + let addTd = await $('td=ADD'); + await expect(addTd).toHaveText('ADD'); + // find row with 'unix.yahoo' present + let spanUnix = await $('span*=unix.yahoo'); + await expect(spanUnix).toHaveText('unix.yahoo'); + + // Verify history is displayed after page refresh + // refresh page + await browser.refresh(); + // find row with 'ADD' + addTd = await $('td=ADD'); + await expect(addTd).toHaveText('ADD'); + // find row with 'unix.yahoo' present + spanUnix = await $('span*=unix.yahoo'); + await expect(spanUnix).toHaveText('unix.yahoo'); + }); + + // after - runs after the last test in order of declaration + after(async() => { + // open browser + await browser.newUser(); + await browser.url(`/`); + // select domain + let domain = 'athenz.dev.functional-test'; + let testDomain = await $(`a*=${domain}`); + await testDomain.click(); + + // navigate to groups page + let groups = await $('div*=Groups'); + await groups.click(); + + // delete the group used in the test + let buttonDeleteGroup = await $('.//*[local-name()="svg" and @id="delete-group-icon-history-test-group"]'); + await buttonDeleteGroup.click(); + let modalDeleteButton = await $('button*=Delete'); + await modalDeleteButton.click(); + }); +}) diff --git a/ui/src/__tests__/spec/tests/review.spec.js b/ui/src/__tests__/spec/tests/review.spec.js index beed3d0982e..abdd58e5848 100644 --- a/ui/src/__tests__/spec/tests/review.spec.js +++ b/ui/src/__tests__/spec/tests/review.spec.js @@ -14,129 +14,130 @@ * limitations under the License. */ -describe('Review user journey', () => { - it('should successfully add, review, and delete role', async () => { - await browser.newUser(); - await browser.url(`/`); - - let domain = 'athenz.dev.functional-test'; - let testDomain = await $(`a*=${domain}`); - let testRoleName = 'testrole2'; - await browser.waitUntil(async () => await testDomain.isClickable()); - await testDomain.click(); - - let addRoleButton = await $('button*=Add Role'); - await browser.waitUntil(async () => await addRoleButton.isClickable()); - addRoleButton.click(); - let roleNameInput = await $('#role-name-input'); - await roleNameInput.addValue(testRoleName); - - let advancedSettingsToggleButton = await $('#advanced-settings-icon'); - advancedSettingsToggleButton.click(); - - let userExpiryDaysInput = await $('#setting-memberExpiryDays'); - await userExpiryDaysInput.addValue('5'); - - let userReviewDaysInput = await $('#setting-memberReviewDays'); - await userReviewDaysInput.addValue('5'); - - let submitButton = await $('button*=Submit'); - await submitButton.click(); - - let testRole = await $(`span*= ${testRoleName}`); - await browser.waitUntil(async () => await testRole.isDisplayed()); - await expect(testRole).toExist(); - - await browser.url('/workflow/role'); - - let allJustificationInput = await $('#all-justification'); - await browser.waitUntil( - async () => await allJustificationInput.isDisplayed() - ); - await allJustificationInput.addValue('test'); - - let submitReviewButton = await $(`#submit-button-${testRoleName}`); - await submitReviewButton.click(); - await expect(submitReviewButton).not.toExist(); // after successful review, button should not exist - await browser.url(`/domain/${domain}/role`); - - let deleteRoleButton = await $(`#${testRoleName}-delete-role-button`); - await browser.waitUntil( - async () => await deleteRoleButton.isClickable() - ); - await deleteRoleButton.click(); - - let confirmDeleteRoleButton = await $( - 'button[data-testid="delete-modal-delete"]' - ); - await confirmDeleteRoleButton.click(); - await expect(testRole).not.toExist(); - }); - - it('should successfully add, review, and delete group', async () => { - let domain = 'athenz.dev.functional-test'; - let testGroupName = 'testgroup'; - await browser.newUser(); - await browser.url(`/domain/${domain}/group`); - - let addGroupButton = await $('button*=Add Group'); - await browser.waitUntil(async () => await addGroupButton.isClickable()); - addGroupButton.click(); - let groupNameInput = await $('#group-name-input'); - await groupNameInput.addValue(testGroupName); - - let submitButton = await $('button*=Submit'); - await submitButton.click(); - - let testGroup = await $(`span*= ${testGroupName}`); - await browser.waitUntil(async () => await testGroup.isDisplayed()); - await expect(testGroup).toExist(); - - let groupSettingsIcon = await $( - `#group-settings-icon-${testGroupName}` - ); - await browser.waitUntil( - async () => await groupSettingsIcon.isClickable() - ); - await groupSettingsIcon.click(); - - let userExpiryDaysInput = await $('#setting-memberExpiryDays'); - await browser.waitUntil( - async () => await userExpiryDaysInput.isDisplayed() - ); - await userExpiryDaysInput.addValue('5'); - - submitButton = await $('button*=Submit'); - await submitButton.click(); - - let confirmUpdateGroupButton = await $( - 'button[data-testid="update-modal-update"]' - ); - await confirmUpdateGroupButton.click(); - - await browser.url('/workflow/group'); - - let allJustificationInput = await $('#all-justification'); - await browser.waitUntil( - async () => await allJustificationInput.isDisplayed() - ); - await allJustificationInput.addValue('test'); - - let submitReviewButton = await $(`#submit-button-${testGroupName}`); - await submitReviewButton.click(); - await expect(submitReviewButton).not.toExist(); // after successful review, button should not exist - await browser.url(`/domain/${domain}/group`); - - let deleteGroupButton = await $(`#delete-group-icon-${testGroupName}`); - await browser.waitUntil( - async () => await deleteGroupButton.isClickable() - ); - await deleteGroupButton.click(); - - let confirmDeleteGroupButton = await $( - 'button[data-testid="delete-modal-delete"]' - ); - await confirmDeleteGroupButton.click(); - await expect(testGroup).not.toExist(); - }); -}); +// TODO add capability to have a member to review consistently +// describe('Review user journey', () => { +// it('should successfully add, review, and delete role', async () => { +// await browser.newUser(); +// await browser.url(`/`); +// +// let domain = 'athenz.dev.functional-test'; +// let testDomain = await $(`a*=${domain}`); +// let testRoleName = 'testrole2'; +// await browser.waitUntil(async () => await testDomain.isClickable()); +// await testDomain.click(); +// +// let addRoleButton = await $('button*=Add Role'); +// await browser.waitUntil(async () => await addRoleButton.isClickable()); +// addRoleButton.click(); +// let roleNameInput = await $('#role-name-input'); +// await roleNameInput.addValue(testRoleName); +// +// let advancedSettingsToggleButton = await $('#advanced-settings-icon'); +// advancedSettingsToggleButton.click(); +// +// let userExpiryDaysInput = await $('#setting-memberExpiryDays'); +// await userExpiryDaysInput.addValue('5'); +// +// let userReviewDaysInput = await $('#setting-memberReviewDays'); +// await userReviewDaysInput.addValue('5'); +// +// let submitButton = await $('button*=Submit'); +// await submitButton.click(); +// +// let testRole = await $(`span*= ${testRoleName}`); +// await browser.waitUntil(async () => await testRole.isDisplayed()); +// await expect(testRole).toExist(); +// +// await browser.url('/workflow/role'); +// +// let allJustificationInput = await $('#all-justification'); +// await browser.waitUntil( +// async () => await allJustificationInput.isDisplayed() +// ); +// await allJustificationInput.addValue('test'); +// +// let submitReviewButton = await $(`#submit-button-${testRoleName}`); +// await submitReviewButton.click(); +// await expect(submitReviewButton).not.toExist(); // after successful review, button should not exist +// await browser.url(`/domain/${domain}/role`); +// +// let deleteRoleButton = await $(`#${testRoleName}-delete-role-button`); +// await browser.waitUntil( +// async () => await deleteRoleButton.isClickable() +// ); +// await deleteRoleButton.click(); +// +// let confirmDeleteRoleButton = await $( +// 'button[data-testid="delete-modal-delete"]' +// ); +// await confirmDeleteRoleButton.click(); +// await expect(testRole).not.toExist(); +// }); +// +// it('should successfully add, review, and delete group', async () => { +// let domain = 'athenz.dev.functional-test'; +// let testGroupName = 'testgroup'; +// await browser.newUser(); +// await browser.url(`/domain/${domain}/group`); +// +// let addGroupButton = await $('button*=Add Group'); +// await browser.waitUntil(async () => await addGroupButton.isClickable()); +// addGroupButton.click(); +// let groupNameInput = await $('#group-name-input'); +// await groupNameInput.addValue(testGroupName); +// +// let submitButton = await $('button*=Submit'); +// await submitButton.click(); +// +// let testGroup = await $(`span*= ${testGroupName}`); +// await browser.waitUntil(async () => await testGroup.isDisplayed()); +// await expect(testGroup).toExist(); +// +// let groupSettingsIcon = await $( +// `#group-settings-icon-${testGroupName}` +// ); +// await browser.waitUntil( +// async () => await groupSettingsIcon.isClickable() +// ); +// await groupSettingsIcon.click(); +// +// let userExpiryDaysInput = await $('#setting-memberExpiryDays'); +// await browser.waitUntil( +// async () => await userExpiryDaysInput.isDisplayed() +// ); +// await userExpiryDaysInput.addValue('5'); +// +// submitButton = await $('button*=Submit'); +// await submitButton.click(); +// +// let confirmUpdateGroupButton = await $( +// 'button[data-testid="update-modal-update"]' +// ); +// await confirmUpdateGroupButton.click(); +// +// await browser.url('/workflow/group'); +// +// let allJustificationInput = await $('#all-justification'); +// await browser.waitUntil( +// async () => await allJustificationInput.isDisplayed() +// ); +// await allJustificationInput.addValue('test'); +// +// let submitReviewButton = await $(`#submit-button-${testGroupName}`); +// await submitReviewButton.click(); +// await expect(submitReviewButton).not.toExist(); // after successful review, button should not exist +// await browser.url(`/domain/${domain}/group`); +// +// let deleteGroupButton = await $(`#delete-group-icon-${testGroupName}`); +// await browser.waitUntil( +// async () => await deleteGroupButton.isClickable() +// ); +// await deleteGroupButton.click(); +// +// let confirmDeleteGroupButton = await $( +// 'button[data-testid="delete-modal-delete"]' +// ); +// await confirmDeleteGroupButton.click(); +// await expect(testGroup).not.toExist(); +// }); +// }); diff --git a/ui/src/__tests__/spec/tests/role.spec.js b/ui/src/__tests__/spec/tests/role.spec.js new file mode 100644 index 00000000000..27e959139a9 --- /dev/null +++ b/ui/src/__tests__/spec/tests/role.spec.js @@ -0,0 +1,140 @@ +/* + * Copyright The Athenz Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +describe('role screen tests', () => { + it('when creating or editing a delegated role, all additional settings except description must be disabled', async () => { + // open browser + await browser.newUser(); + await browser.url(`/`); + // select domain + let domain = 'athenz.dev.functional-test'; + let testDomain = await $(`a*=${domain}`); + await browser.waitUntil(async () => await testDomain.isClickable()); + await testDomain.click(); + + // open Add Role screen + let addRoleButton = await $('button*=Add Role'); + await browser.waitUntil(async () => await addRoleButton.isClickable()); + await addRoleButton.click(); + // select Delegated + let delegatedButton = await $('div*=Delegated'); + await delegatedButton.click(); + // verify all settings except Description are disabled + let advancedSettingsIcon = await $('#advanced-settings-icon'); + await advancedSettingsIcon.click(); + let switchSettingAuditEnabled = await $('#switch-settingauditEnabled'); + await expect(switchSettingAuditEnabled).toBeDisabled(); + let switchSettingReviewEnabled = await $('#switch-settingreviewEnabled'); + await expect(switchSettingReviewEnabled).toBeDisabled(); + let switchSettingDeleteProtection = await $('#switch-settingdeleteProtection'); + await expect(switchSettingDeleteProtection).toBeDisabled(); + let switchSettingSelfServe = await $('#switch-settingselfServe'); + await expect(switchSettingSelfServe).toBeDisabled(); + let switchSettingSelfRenew = await $('#switch-settingselfRenew'); + await expect(switchSettingSelfRenew).toBeDisabled(); + let inputSelfRenewMins = await $('#setting-selfRenewMins'); + await expect(inputSelfRenewMins).toBeDisabled(); + let inputMemberExpiryDays = await $('#setting-memberExpiryDays'); + await expect(inputMemberExpiryDays).toBeDisabled(); + let inputGroupExpiryDays = await $('#setting-groupExpiryDays'); + await expect(inputGroupExpiryDays).toBeDisabled(); + let inputGroupReviewDays = await $('#setting-groupReviewDays'); + await expect(inputGroupReviewDays).toBeDisabled(); + let inputServiceExpiryDays = await $('#setting-serviceExpiryDays'); + await expect(inputServiceExpiryDays).toBeDisabled(); + let inputServiceReviewDays = await $('#setting-serviceReviewDays'); + await expect(inputServiceReviewDays).toBeDisabled(); + let inputTokenExpiryMins = await $('#setting-tokenExpiryMins'); + await expect(inputTokenExpiryMins).toBeDisabled(); + let inputCertExpiryMins = await $('#setting-certExpiryMins'); + await expect(inputCertExpiryMins).toBeDisabled(); + let dropdownUserAuthorityFilter = await $('[name="setting-userAuthorityFilter"]'); + await expect(dropdownUserAuthorityFilter).toBeDisabled(); + let dropdownUserAuthorityExpiration = await $('[name="setting-userAuthorityExpiration"]'); + await expect(dropdownUserAuthorityExpiration).toBeDisabled(); + let inputSettingDescription = await $('#setting-description'); + await expect(inputSettingDescription).toBeEnabled(); + let inputMaxMembers = await $('#setting-maxMembers'); + await expect(inputMaxMembers).toBeDisabled(); + + // add role info + let inputRoleName = await $('#role-name-input'); + let roleName = 'delegated-role'; + await inputRoleName.addValue(roleName); + let inputDelegateTo = await $('#delegated-to-input'); + await inputDelegateTo.addValue('athenz.dev'); + let buttonSubmit = await $('button*=Submit'); + // submit role + await buttonSubmit.click(); + + // find row with 'delegated-role' in name and click settings svg + let buttonSettingsOfDelegatedRole = await $('.//*[local-name()="svg" and @id="delegated-role-setting-role-button"]'); + await buttonSettingsOfDelegatedRole.click(); + + // verify all settings except Description are disabled + switchSettingReviewEnabled = await $('#switch-settingreviewEnabled'); + await expect(switchSettingReviewEnabled).toBeDisabled(); + switchSettingDeleteProtection = await $('#switch-settingdeleteProtection'); + await expect(switchSettingDeleteProtection).toBeDisabled(); + switchSettingSelfServe = await $('#switch-settingselfServe'); + await expect(switchSettingSelfServe).toBeDisabled(); + switchSettingSelfRenew = await $('#switch-settingselfRenew'); + await expect(switchSettingSelfRenew).toBeDisabled(); + inputSelfRenewMins = await $('#setting-selfRenewMins'); + await expect(inputSelfRenewMins).toBeDisabled(); + inputMemberExpiryDays = await $('#setting-memberExpiryDays'); + await expect(inputMemberExpiryDays).toBeDisabled(); + inputGroupExpiryDays = await $('#setting-groupExpiryDays'); + await expect(inputGroupExpiryDays).toBeDisabled(); + inputGroupReviewDays = await $('#setting-groupReviewDays'); + await expect(inputGroupReviewDays).toBeDisabled(); + inputServiceExpiryDays = await $('#setting-serviceExpiryDays'); + await expect(inputServiceExpiryDays).toBeDisabled(); + inputServiceReviewDays = await $('#setting-serviceReviewDays'); + await expect(inputServiceReviewDays).toBeDisabled(); + inputTokenExpiryMins = await $('#setting-tokenExpiryMins'); + await expect(inputTokenExpiryMins).toBeDisabled(); + inputCertExpiryMins = await $('#setting-certExpiryMins'); + await expect(inputCertExpiryMins).toBeDisabled(); + dropdownUserAuthorityFilter = await $('[name="setting-userAuthorityFilter"]'); + await expect(dropdownUserAuthorityFilter).toBeDisabled(); + dropdownUserAuthorityExpiration = await $('[name="setting-userAuthorityExpiration"]'); + await expect(dropdownUserAuthorityExpiration).toBeDisabled(); + inputSettingDescription = await $('#setting-description'); + await expect(inputSettingDescription).toBeEnabled(); + inputMaxMembers = await $('#setting-maxMembers'); + await expect(inputMaxMembers).toBeDisabled(); + }); + + // after - runs after the last test in order of declaration + after(async() => { + // open browser + await browser.newUser(); + await browser.url(`/`); + // select domain + let domain = 'athenz.dev.functional-test'; + let testDomain = await $(`a*=${domain}`); + await browser.waitUntil(async () => await testDomain.isClickable()); + await testDomain.click(); + + // delete the delegate role used in the test + // find row with 'delegated-role' in name and click delete on svg + let buttonDeleteDelegatedRole = await $('.//*[local-name()="svg" and @id="delegated-role-delete-role-button"]'); + await buttonDeleteDelegatedRole.click(); + let modalDeleteButton = await $('button*=Delete'); + await modalDeleteButton.click(); + }); +}) diff --git a/ui/src/__tests__/spec/tests/roles.spec.js b/ui/src/__tests__/spec/tests/roles.spec.js new file mode 100644 index 00000000000..d620e8acdcc --- /dev/null +++ b/ui/src/__tests__/spec/tests/roles.spec.js @@ -0,0 +1,83 @@ +/* + * Copyright The Athenz Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +describe('role screen tests', () => { + it('role history should be visible when navigating to it and after page refresh', async () => { + // open browser + await browser.newUser(); + await browser.url(`/`); + // select domain + let domain = 'athenz.dev.functional-test'; + let testDomain = await $(`a*=${domain}`); + await testDomain.click(); + + // ADD test role + // open Add Role screen + let addiRoleButton = await $('button*=Add Role'); + await addiRoleButton.click(); + // add group info + let inputRoleName = await $('#role-name-input'); + let roleName = 'history-test-role'; + await inputRoleName.addValue(roleName); + // add user + let addMemberInput = await $('[name="member-name"]'); + await addMemberInput.addValue('unix.yahoo'); + let userOption = await $('div*=unix.yahoo'); + await userOption.click(); + // submit role + let buttonSubmit = await $('button*=Submit'); + await buttonSubmit.click(); + + // Verify history entry of added role member is present + // open history + let historySvg = await $('.//*[local-name()="svg" and @id="history-test-role-history-role-button"]'); + await historySvg.click(); + // find row with 'ADD' + let addTd = await $('td=ADD'); + await expect(addTd).toHaveText('ADD'); + // find row with 'unix.yahoo' present + let spanUnix = await $('span*=unix.yahoo'); + await expect(spanUnix).toHaveText('unix.yahoo'); + + // Verify history is displayed after page refresh + // refresh page + await browser.refresh(); + // find row with 'ADD' + addTd = await $('td=ADD'); + await expect(addTd).toHaveText('ADD'); + // find row with 'unix.yahoo' present + spanUnix = await $('span*=unix.yahoo'); + await expect(spanUnix).toHaveText('unix.yahoo'); + }); + + // after - runs after the last test in order of declaration + after(async() => { + // open browser + await browser.newUser(); + await browser.url(`/`); + // select domain + let domain = 'athenz.dev.functional-test'; + let testDomain = await $(`a*=${domain}`); + await testDomain.click(); + + // delete the role used in the test + let buttonDeleteRole = await $('.//*[local-name()="svg" and @id="history-test-role-delete-role-button"]'); + await buttonDeleteRole.click(); + let modalDeleteButton = await $('button*=Delete'); + await modalDeleteButton.click(); + }); +}) diff --git a/ui/src/__tests__/spec/wdio.conf.js b/ui/src/__tests__/spec/wdio.conf.js index a6ff8b60b9a..5c9617cfc9c 100644 --- a/ui/src/__tests__/spec/wdio.conf.js +++ b/ui/src/__tests__/spec/wdio.conf.js @@ -79,6 +79,15 @@ if (!sauceLabsUser) { localOrRemote.capabilities = [ { browserName: 'chrome', + 'goog:chromeOptions': { + args: [ + '--disable-infobars', // Disables "Chrome is being controlled by automated software" infobar + '--disable-default-apps', // Disables default apps (including search engine prompts) + '--no-first-run', // Bypass first-time setup including "Choose your search engine" + '--disable-popup-blocking', // Disables popup blocking + '--disable-search-engine-choice-screen' // Disables choose your search engine popup + ] + }, browserVersion: 'latest', acceptInsecureCerts: true, }, diff --git a/ui/src/components/group/GroupRow.js b/ui/src/components/group/GroupRow.js index 3ef9b411380..95795aaad70 100644 --- a/ui/src/components/group/GroupRow.js +++ b/ui/src/components/group/GroupRow.js @@ -358,6 +358,7 @@ class GroupRow extends React.Component { trigger={ Delegated to diff --git a/ui/src/components/role/AddRoleAdvancedSettings.js b/ui/src/components/role/AddRoleAdvancedSettings.js index c5536f84c0a..988da6c0654 100644 --- a/ui/src/components/role/AddRoleAdvancedSettings.js +++ b/ui/src/components/role/AddRoleAdvancedSettings.js @@ -90,6 +90,7 @@ export default class AddRoleAdvancedSettings extends React.Component { label='Audit' type='switch' disabled={ + this.props.delegated || !this.props.isDomainAuditEnabled || this.props.members.length > 0 } @@ -111,6 +112,7 @@ export default class AddRoleAdvancedSettings extends React.Component { userProfileLink={this.props.userProfileLink} inModal={true} tooltip={ADD_ROLE_REVIEW_ENABLED_TOOLTIP} + disabled={this.props.delegated} />, , , , , , , , , , , , , , , , ]; } diff --git a/ui/src/components/role/RoleRow.js b/ui/src/components/role/RoleRow.js index 3683cf689d4..8ac0a9565ff 100644 --- a/ui/src/components/role/RoleRow.js +++ b/ui/src/components/role/RoleRow.js @@ -415,6 +415,7 @@ class RoleRow extends React.Component { trigger={ ); diff --git a/ui/src/components/settings/SettingTable.js b/ui/src/components/settings/SettingTable.js index 1ac61590dee..95b2cdcd495 100644 --- a/ui/src/components/settings/SettingTable.js +++ b/ui/src/components/settings/SettingTable.js @@ -388,6 +388,7 @@ class SettingTable extends React.Component { value={this.state.copyCollectionDetails.reviewEnabled} onValueChange={this.onValueChange} _csrf={this.props._csrf} + disabled={this.props.roleIsDelegated || false} /> ); @@ -401,6 +402,7 @@ class SettingTable extends React.Component { ); let selfServiceDesc = @@ -450,6 +453,7 @@ class SettingTable extends React.Component { value={this.state.copyCollectionDetails.selfServe} onValueChange={this.onValueChange} _csrf={this.props._csrf} + disabled={this.props.roleIsDelegated || false} /> ); @@ -469,6 +473,7 @@ class SettingTable extends React.Component { value={this.state.copyCollectionDetails.selfRenew} onValueChange={this.onValueChange} _csrf={this.props._csrf} + disabled={this.props.roleIsDelegated || false} /> ); @@ -483,7 +488,7 @@ class SettingTable extends React.Component { unit='Mins' desc={SELF_RENEW_MINS_DESC} value={this.state.copyCollectionDetails.selfRenewMins} - disabled={!this.state.copyCollectionDetails.selfRenew} + disabled={this.props.roleIsDelegated || !this.state.copyCollectionDetails.selfRenew} onValueChange={this.onValueChange} _csrf={this.props._csrf} /> @@ -505,6 +510,7 @@ class SettingTable extends React.Component { value={this.state.copyCollectionDetails.memberExpiryDays} onValueChange={this.onValueChange} _csrf={this.props._csrf} + disabled={this.props.roleIsDelegated || false} /> ); @@ -521,6 +527,7 @@ class SettingTable extends React.Component { value={this.state.copyCollectionDetails.memberReviewDays} onValueChange={this.onValueChange} _csrf={this.props._csrf} + disabled={this.props.roleIsDelegated || false} /> ); @@ -541,6 +548,7 @@ class SettingTable extends React.Component { value={this.state.copyCollectionDetails.groupExpiryDays} onValueChange={this.onValueChange} _csrf={this.props._csrf} + disabled={this.props.roleIsDelegated || false} /> ); @@ -557,6 +565,7 @@ class SettingTable extends React.Component { value={this.state.copyCollectionDetails.groupReviewDays} onValueChange={this.onValueChange} _csrf={this.props._csrf} + disabled={this.props.roleIsDelegated || false} /> ); @@ -576,6 +585,7 @@ class SettingTable extends React.Component { value={this.state.copyCollectionDetails.serviceExpiryDays} onValueChange={this.onValueChange} _csrf={this.props._csrf} + disabled={this.props.roleIsDelegated || false} /> ); @@ -592,6 +602,7 @@ class SettingTable extends React.Component { value={this.state.copyCollectionDetails.serviceReviewDays} onValueChange={this.onValueChange} _csrf={this.props._csrf} + disabled={this.props.roleIsDelegated || false} /> ); @@ -612,6 +623,7 @@ class SettingTable extends React.Component { value={this.state.copyCollectionDetails.tokenExpiryMins} onValueChange={this.onValueChange} _csrf={this.props._csrf} + disabled={this.props.roleIsDelegated || false} /> ); @@ -628,6 +640,7 @@ class SettingTable extends React.Component { value={this.state.copyCollectionDetails.certExpiryMins} onValueChange={this.onValueChange} _csrf={this.props._csrf} + disabled={this.props.roleIsDelegated || false} /> ); @@ -663,6 +676,7 @@ class SettingTable extends React.Component { value={this.state.copyCollectionDetails.userAuthorityFilter} onValueChange={this.onValueChange} _csrf={this.props._csrf} + disabled={this.props.roleIsDelegated || false} /> ); @@ -682,6 +696,7 @@ class SettingTable extends React.Component { } onValueChange={this.onValueChange} _csrf={this.props._csrf} + disabled={this.props.roleIsDelegated || false} /> ); @@ -717,6 +732,7 @@ class SettingTable extends React.Component { value={this.state.copyCollectionDetails.maxMembers} onValueChange={this.onValueChange} _csrf={this.props._csrf} + disabled={this.props.roleIsDelegated || false} /> ); diff --git a/ui/src/pages/domain/[domain]/role/[role]/settings.js b/ui/src/pages/domain/[domain]/role/[role]/settings.js index 59fc2dad8af..b52197715d1 100644 --- a/ui/src/pages/domain/[domain]/role/[role]/settings.js +++ b/ui/src/pages/domain/[domain]/role/[role]/settings.js @@ -174,6 +174,8 @@ class SettingPage extends React.Component { collectionDetails={roleDetails} _csrf={_csrf} category={'role'} + // to disable all settings except description for delegated role + roleIsDelegated={!!this.props.roleDetails.trust} /> diff --git a/ui/src/redux/reducers/groups.js b/ui/src/redux/reducers/groups.js index 7f09a458385..55018d9a635 100644 --- a/ui/src/redux/reducers/groups.js +++ b/ui/src/redux/reducers/groups.js @@ -159,11 +159,12 @@ export const groups = (state = {}, action) => { return newState; } case LOAD_GROUP: { - const { groupData, groupName } = payload; - let newState = produce(state, (draft) => { - draft.groups[groupName] = groupData; + return produce(state, (draft) => { + if (!!!draft.groups) { + draft.groups = {}; + } + draft.groups[payload.groupName] = payload.groupData; }); - return newState; } case UPDATE_SETTING_TO_STORE: { const { collectionName, collectionSettings, category } = payload; diff --git a/ui/src/redux/reducers/roles.js b/ui/src/redux/reducers/roles.js index 6501e308b41..0ca15c06987 100644 --- a/ui/src/redux/reducers/roles.js +++ b/ui/src/redux/reducers/roles.js @@ -65,11 +65,12 @@ export const roles = (state = {}, action) => { return newState; } case LOAD_ROLE: { - const { roleData, roleName } = payload; - let newState = produce(state, (draft) => { - draft.roles[roleName] = roleData; + return produce(state, (draft) => { + if (!!!draft.roles) { + draft.roles = {}; + } + draft.roles[payload.roleName] = payload.roleData; }); - return newState; } case LOAD_ROLES_TO_REVIEW: { const { rolesToReview } = payload; diff --git a/utils/msd-agent/client/client.go b/utils/msd-agent/client/client.go index 85753148f3b..ec5aa5943ad 100644 --- a/utils/msd-agent/client/client.go +++ b/utils/msd-agent/client/client.go @@ -5,20 +5,21 @@ package client import ( "fmt" - "github.com/AthenZ/athenz/libs/go/athenz-common/log" - "github.com/AthenZ/athenz/libs/go/tls/config" - svc "github.com/AthenZ/athenz/utils/msd-agent/svc" "net/http" "os" "regexp" + "github.com/AthenZ/athenz/libs/go/athenz-common/log" + "github.com/AthenZ/athenz/libs/go/tls/config" + svc "github.com/AthenZ/athenz/utils/msd-agent/svc" + "github.com/AthenZ/athenz/clients/go/msd" ) const USER_AGENT = "User-Agent" type MsdClient interface { - PutWorkload(domain string, service string, options *msd.WorkloadOptions) error + PutWorkload(domain string, service string, options *msd.WorkloadOptions, resourceOwner string) error } type Client struct { @@ -30,9 +31,9 @@ type Client struct { var version = "" -func (c Client) PutWorkload(domain string, service string, options *msd.WorkloadOptions) error { +func (c Client) PutWorkload(domain string, service string, options *msd.WorkloadOptions, resourceOwner string) error { msdClient := clientWithUserAgent(c) - return msdClient.PutDynamicWorkload(msd.DomainName(domain), msd.EntityName(service), options) + return msdClient.PutDynamicWorkload(msd.DomainName(domain), msd.EntityName(service), options, resourceOwner) } func clientWithUserAgent(c Client) msd.MSDClient { diff --git a/utils/msd-agent/client/client_test.go b/utils/msd-agent/client/client_test.go index 22c3a85f4ff..6c81db5741c 100644 --- a/utils/msd-agent/client/client_test.go +++ b/utils/msd-agent/client/client_test.go @@ -15,7 +15,7 @@ func TestMsdClientGotError(t *testing.T) { domain := "someDomain" service := "someService" clientMock := mockMsdClient(t, domain, service, fmt.Errorf("msd error")) - res := clientMock.PutWorkload(domain, service, nil) + res := clientMock.PutWorkload(domain, service, nil, "msd") assert.NotNilf(t, res, "should get error here") assert.Equal(t, res.Error(), "msd error") } @@ -24,13 +24,13 @@ func TestMsdClientNoError(t *testing.T) { domain := "someDomain" service := "someService" clientMock := mockMsdClient(t, domain, service, nil) - res := clientMock.PutWorkload(domain, service, nil) + res := clientMock.PutWorkload(domain, service, nil, "msd") assert.Nilf(t, res, "should not get error here") } func mockMsdClient(t *testing.T, domain string, service string, result error) *MockMsdClient { mockCtrl := gomock.NewController(t) clientMock := NewMockMsdClient(mockCtrl) - clientMock.EXPECT().PutWorkload(domain, service, nil).Return(result) + clientMock.EXPECT().PutWorkload(domain, service, nil, "msd").Return(result) return clientMock } diff --git a/utils/msd-agent/client/mock_client.go b/utils/msd-agent/client/mock_client.go index 52779ce07d2..d3d9c4f20db 100644 --- a/utils/msd-agent/client/mock_client.go +++ b/utils/msd-agent/client/mock_client.go @@ -1,7 +1,12 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: client.go - -// Package mock_client is a generated GoMock package. +// Source: client/client.go +// +// Generated by this command: +// +// mockgen -destination=./client/mock_client.go -package=client -source=client/client.go MsdClient +// + +// Package client is a generated GoMock package. package client import ( @@ -35,15 +40,15 @@ func (m *MockMsdClient) EXPECT() *MockMsdClientMockRecorder { } // PutWorkload mocks base method. -func (m *MockMsdClient) PutWorkload(domain, service string, options *msd.WorkloadOptions) error { +func (m *MockMsdClient) PutWorkload(domain, service string, options *msd.WorkloadOptions, resourceOwner string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PutWorkload", domain, service, options) + ret := m.ctrl.Call(m, "PutWorkload", domain, service, options, resourceOwner) ret0, _ := ret[0].(error) return ret0 } // PutWorkload indicates an expected call of PutWorkload. -func (mr *MockMsdClientMockRecorder) PutWorkload(domain, service, options interface{}) *gomock.Call { +func (mr *MockMsdClientMockRecorder) PutWorkload(domain, service, options, resourceOwner any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutWorkload", reflect.TypeOf((*MockMsdClient)(nil).PutWorkload), domain, service, options) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutWorkload", reflect.TypeOf((*MockMsdClient)(nil).PutWorkload), domain, service, options, resourceOwner) }