From f45ac0f4b541c81ec20fb93054c7261375808cd7 Mon Sep 17 00:00:00 2001 From: Henry Avetisyan Date: Thu, 29 Jun 2017 12:33:38 -0700 Subject: [PATCH 1/2] quota support for domain object #164 --- clients/go/zms/client.go | 96 +++ clients/go/zms/model.go | 105 ++++ clients/go/zms/zms_schema.go | 118 ++++ .../yahoo/athenz/zms/ResourceException.java | 10 +- .../java/com/yahoo/athenz/zms/ZMSClient.java | 199 ++++-- .../athenz/zms/ZMSRDLGeneratedClient.java | 60 ++ .../com/yahoo/athenz/zms/ZMSClientTest.java | 59 ++ .../yahoo/athenz/zts/ResourceException.java | 10 +- .../main/java/com/yahoo/athenz/zms/Quota.java | 148 +++++ .../java/com/yahoo/athenz/zms/ZMSSchema.java | 195 ++++++ core/zms/src/main/rdl/Access.rdli | 3 + core/zms/src/main/rdl/Domain.rdli | 12 + core/zms/src/main/rdl/DomainDataCheck.rdli | 1 + core/zms/src/main/rdl/Entity.rdli | 4 + core/zms/src/main/rdl/Policy.rdli | 8 + core/zms/src/main/rdl/Quota.rdli | 61 ++ core/zms/src/main/rdl/Role.rdli | 9 + core/zms/src/main/rdl/ServiceIdentity.rdli | 8 + core/zms/src/main/rdl/SignedDomains.rdli | 1 + core/zms/src/main/rdl/Template.rdli | 2 + core/zms/src/main/rdl/Tenancy.rdli | 5 + core/zms/src/main/rdl/TenantRoles.rdli | 9 + core/zms/src/main/rdl/Token.rdli | 3 + core/zms/src/main/rdl/User.rdli | 3 + core/zms/src/main/rdl/ZMS.rdl | 1 + .../com/yahoo/athenz/zms/ZMSCoreTest.java | 45 ++ libs/go/zmscli/cli.go | 52 ++ libs/go/zmscli/dump.go | 44 ++ libs/go/zmscli/quota.go | 80 +++ servers/zms/conf/zms.properties | 34 +- servers/zms/schema/zms_server.mwb | Bin 22936 -> 24514 bytes servers/zms/schema/zms_server.sql | 27 +- .../athenz/provider/ResourceException.java | 10 +- .../java/com/yahoo/athenz/zms/DBService.java | 120 +++- .../com/yahoo/athenz/zms/QuotaChecker.java | 243 +++++++ .../yahoo/athenz/zms/ResourceException.java | 10 +- .../java/com/yahoo/athenz/zms/ZMSConsts.java | 21 + .../java/com/yahoo/athenz/zms/ZMSHandler.java | 3 + .../java/com/yahoo/athenz/zms/ZMSImpl.java | 127 +++- .../com/yahoo/athenz/zms/ZMSResources.java | 218 +++++++ .../zms/store/ObjectStoreConnection.java | 12 +- .../athenz/zms/store/file/FileConnection.java | 58 +- .../zms/store/file/FileObjectStore.java | 25 +- .../athenz/zms/store/jdbc/JDBCConnection.java | 119 ++++ .../com/yahoo/athenz/zms/utils/ZMSUtils.java | 4 + .../com/yahoo/athenz/zms/DBServiceTest.java | 132 +++- .../yahoo/athenz/zms/QuotaCheckerTest.java | 592 ++++++++++++++++++ .../com/yahoo/athenz/zms/ZMSBinderTest.java | 17 + .../com/yahoo/athenz/zms/ZMSImplTest.java | 102 +++ .../zms/store/file/FileConnectionTest.java | 190 +++--- .../zms/store/file/FileObjectStoreTest.java | 18 +- .../zms/store/jdbc/JDBCConnectionTest.java | 275 ++++++++ .../instance/provider/ResourceException.java | 10 +- .../yahoo/athenz/zts/ResourceException.java | 10 +- 54 files changed, 3563 insertions(+), 165 deletions(-) create mode 100644 core/zms/src/main/java/com/yahoo/athenz/zms/Quota.java create mode 100644 core/zms/src/main/rdl/Quota.rdli create mode 100644 libs/go/zmscli/quota.go create mode 100644 servers/zms/src/main/java/com/yahoo/athenz/zms/QuotaChecker.java create mode 100644 servers/zms/src/test/java/com/yahoo/athenz/zms/QuotaCheckerTest.java create mode 100644 servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSBinderTest.java diff --git a/clients/go/zms/client.go b/clients/go/zms/client.go index dae773e1e80..b80219dbca4 100644 --- a/clients/go/zms/client.go +++ b/clients/go/zms/client.go @@ -2480,3 +2480,99 @@ func (client ZMSClient) DeleteUser(name SimpleName, auditRef string) error { return errobj } } + +func (client ZMSClient) GetQuota(name DomainName) (*Quota, error) { + var data *Quota + url := client.URL + "/domain/" + fmt.Sprint(name) + "/quota" + resp, err := client.httpGet(url, nil) + if err != nil { + return data, err + } + contentBytes, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + return data, err + } + switch resp.StatusCode { + case 200: + err = json.Unmarshal(contentBytes, &data) + if err != nil { + return data, err + } + return data, nil + default: + var errobj rdl.ResourceError + json.Unmarshal(contentBytes, &errobj) + if errobj.Code == 0 { + errobj.Code = resp.StatusCode + } + if errobj.Message == "" { + errobj.Message = string(contentBytes) + } + return data, errobj + } +} + +func (client ZMSClient) PutQuota(name DomainName, auditRef string, quota *Quota) error { + headers := map[string]string{ + "Y-Audit-Ref": auditRef, + } + url := client.URL + "/domain/" + fmt.Sprint(name) + "/quota" + contentBytes, err := json.Marshal(quota) + if err != nil { + return err + } + resp, err := client.httpPut(url, headers, contentBytes) + if err != nil { + return err + } + contentBytes, err = ioutil.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + return err + } + switch resp.StatusCode { + case 204: + return nil + default: + var errobj rdl.ResourceError + json.Unmarshal(contentBytes, &errobj) + if errobj.Code == 0 { + errobj.Code = resp.StatusCode + } + if errobj.Message == "" { + errobj.Message = string(contentBytes) + } + return errobj + } +} + +func (client ZMSClient) DeleteQuota(name DomainName, auditRef string) error { + headers := map[string]string{ + "Y-Audit-Ref": auditRef, + } + url := client.URL + "/domain/" + fmt.Sprint(name) + "/quota" + resp, err := client.httpDelete(url, headers) + if err != nil { + return err + } + contentBytes, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + return err + } + switch resp.StatusCode { + case 204: + return nil + default: + var errobj rdl.ResourceError + json.Unmarshal(contentBytes, &errobj) + if errobj.Code == 0 { + errobj.Code = resp.StatusCode + } + if errobj.Message == "" { + errobj.Message = string(contentBytes) + } + return errobj + } +} diff --git a/clients/go/zms/model.go b/clients/go/zms/model.go index b51b5384637..51c6f128f99 100644 --- a/clients/go/zms/model.go +++ b/clients/go/zms/model.go @@ -4075,3 +4075,108 @@ func (self *UserMeta) UnmarshalJSON(b []byte) error { func (self *UserMeta) Validate() error { return nil } + +// +// Quota - The representation for a quota object +// +type Quota struct { + + // + // name of the domain object + // + Name DomainName `json:"name"` + + // + // number of subdomains allowed (applied at top level domain level) + // + Subdomain int32 `json:"subdomain"` + + // + // number of roles allowed + // + Role int32 `json:"role"` + + // + // number of members a role may have + // + RoleMember int32 `json:"roleMember"` + + // + // number of policies allowed + // + Policy int32 `json:"policy"` + + // + // total number of assertions a policy may have + // + Assertion int32 `json:"assertion"` + + // + // total number of entity objects + // + Entity int32 `json:"entity"` + + // + // number of services allowed + // + Service int32 `json:"service"` + + // + // number of hosts allowed per service + // + ServiceHost int32 `json:"serviceHost"` + + // + // number of public keys per service + // + PublicKey int32 `json:"publicKey"` + + // + // the last modification timestamp of the quota object + // + Modified *rdl.Timestamp `json:"modified,omitempty" rdl:"optional"` +} + +// +// NewQuota - creates an initialized Quota instance, returns a pointer to it +// +func NewQuota(init ...*Quota) *Quota { + var o *Quota + if len(init) == 1 { + o = init[0] + } else { + o = new(Quota) + } + return o +} + +type rawQuota Quota + +// +// UnmarshalJSON is defined for proper JSON decoding of a Quota +// +func (self *Quota) UnmarshalJSON(b []byte) error { + var r rawQuota + err := json.Unmarshal(b, &r) + if err == nil { + o := Quota(r) + *self = o + err = self.Validate() + } + return err +} + +// +// Validate - checks for missing required fields, etc +// +func (self *Quota) Validate() error { + if self.Name == "" { + return fmt.Errorf("Quota.name is missing but is a required field") + } else { + val := rdl.Validate(ZMSSchema(), "DomainName", self.Name) + if !val.Valid { + return fmt.Errorf("Quota.name does not contain a valid DomainName (%v)", val.Error) + } + } + return nil +} diff --git a/clients/go/zms/zms_schema.go b/clients/go/zms/zms_schema.go index e515be0e056..57f52d04195 100644 --- a/clients/go/zms/zms_schema.go +++ b/clients/go/zms/zms_schema.go @@ -424,6 +424,21 @@ func init() { tUserMeta.Field("enabled", "Bool", true, true, "") sb.AddType(tUserMeta.Build()) + tQuota := rdl.NewStructTypeBuilder("Struct", "Quota") + tQuota.Comment("The representation for a quota object") + tQuota.Field("name", "DomainName", false, nil, "name of the domain object") + tQuota.Field("subdomain", "Int32", false, nil, "number of subdomains allowed (applied at top level domain level)") + tQuota.Field("role", "Int32", false, nil, "number of roles allowed") + tQuota.Field("roleMember", "Int32", false, nil, "number of members a role may have") + tQuota.Field("policy", "Int32", false, nil, "number of policies allowed") + tQuota.Field("assertion", "Int32", false, nil, "total number of assertions a policy may have") + tQuota.Field("entity", "Int32", false, nil, "total number of entity objects") + tQuota.Field("service", "Int32", false, nil, "number of services allowed") + tQuota.Field("serviceHost", "Int32", false, nil, "number of hosts allowed per service") + tQuota.Field("publicKey", "Int32", false, nil, "number of public keys per service") + tQuota.Field("modified", "Timestamp", true, nil, "the last modification timestamp of the quota object") + sb.AddType(tQuota.Build()) + rGetDomain := rdl.NewResourceBuilder("Domain", "GET", "/domain/{domain}") rGetDomain.Comment("Get info for the specified domain, by name. This request only returns the configured domain attributes and not any domain objects like roles, policies or service identities.") rGetDomain.Input("domain", "DomainName", true, "", "", false, nil, "name of the domain") @@ -431,6 +446,7 @@ func init() { rGetDomain.Exception("BAD_REQUEST", "ResourceError", "") rGetDomain.Exception("FORBIDDEN", "ResourceError", "") rGetDomain.Exception("NOT_FOUND", "ResourceError", "") + rGetDomain.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetDomain.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetDomain.Build()) @@ -445,6 +461,7 @@ func init() { rGetDomainList.Input("roleMember", "ResourceName", false, "member", "", true, nil, "restrict the domain names where the specified user is in a role - see roleName") rGetDomainList.Input("roleName", "ResourceName", false, "role", "", true, nil, "restrict the domain names where the specified user is in this role - see roleMember") rGetDomainList.Input("modifiedSince", "String", false, "", "If-Modified-Since", false, nil, "This header specifies to the server to return any domains modified since this HTTP date") + rGetDomainList.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetDomainList.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetDomainList.Build()) @@ -455,6 +472,7 @@ func init() { rPostTopLevelDomain.Auth("create", "sys.auth:domain", false, "") rPostTopLevelDomain.Exception("BAD_REQUEST", "ResourceError", "") rPostTopLevelDomain.Exception("FORBIDDEN", "ResourceError", "") + rPostTopLevelDomain.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPostTopLevelDomain.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPostTopLevelDomain.Build()) @@ -467,6 +485,7 @@ func init() { rPostSubDomain.Exception("BAD_REQUEST", "ResourceError", "") rPostSubDomain.Exception("FORBIDDEN", "ResourceError", "") rPostSubDomain.Exception("NOT_FOUND", "ResourceError", "") + rPostSubDomain.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPostSubDomain.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPostSubDomain.Build()) @@ -479,6 +498,7 @@ func init() { rPostUserDomain.Exception("BAD_REQUEST", "ResourceError", "") rPostUserDomain.Exception("FORBIDDEN", "ResourceError", "") rPostUserDomain.Exception("NOT_FOUND", "ResourceError", "") + rPostUserDomain.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPostUserDomain.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPostUserDomain.Build()) @@ -491,6 +511,7 @@ func init() { rDeleteTopLevelDomain.Exception("BAD_REQUEST", "ResourceError", "") rDeleteTopLevelDomain.Exception("FORBIDDEN", "ResourceError", "") rDeleteTopLevelDomain.Exception("NOT_FOUND", "ResourceError", "") + rDeleteTopLevelDomain.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rDeleteTopLevelDomain.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rDeleteTopLevelDomain.Build()) @@ -504,6 +525,7 @@ func init() { rDeleteSubDomain.Exception("BAD_REQUEST", "ResourceError", "") rDeleteSubDomain.Exception("FORBIDDEN", "ResourceError", "") rDeleteSubDomain.Exception("NOT_FOUND", "ResourceError", "") + rDeleteSubDomain.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rDeleteSubDomain.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rDeleteSubDomain.Build()) @@ -516,6 +538,7 @@ func init() { rDeleteUserDomain.Exception("BAD_REQUEST", "ResourceError", "") rDeleteUserDomain.Exception("FORBIDDEN", "ResourceError", "") rDeleteUserDomain.Exception("NOT_FOUND", "ResourceError", "") + rDeleteUserDomain.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rDeleteUserDomain.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rDeleteUserDomain.Build()) @@ -530,6 +553,7 @@ func init() { rPutDomainMeta.Exception("CONFLICT", "ResourceError", "") rPutDomainMeta.Exception("FORBIDDEN", "ResourceError", "") rPutDomainMeta.Exception("NOT_FOUND", "ResourceError", "") + rPutDomainMeta.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPutDomainMeta.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPutDomainMeta.Build()) @@ -544,6 +568,7 @@ func init() { rPutDomainTemplate.Exception("CONFLICT", "ResourceError", "") rPutDomainTemplate.Exception("FORBIDDEN", "ResourceError", "") rPutDomainTemplate.Exception("NOT_FOUND", "ResourceError", "") + rPutDomainTemplate.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPutDomainTemplate.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPutDomainTemplate.Build()) @@ -553,6 +578,7 @@ func init() { rGetDomainTemplateList.Auth("", "", true, "") rGetDomainTemplateList.Exception("BAD_REQUEST", "ResourceError", "") rGetDomainTemplateList.Exception("NOT_FOUND", "ResourceError", "") + rGetDomainTemplateList.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetDomainTemplateList.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetDomainTemplateList.Build()) @@ -567,6 +593,7 @@ func init() { rDeleteDomainTemplate.Exception("CONFLICT", "ResourceError", "") rDeleteDomainTemplate.Exception("FORBIDDEN", "ResourceError", "") rDeleteDomainTemplate.Exception("NOT_FOUND", "ResourceError", "") + rDeleteDomainTemplate.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rDeleteDomainTemplate.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rDeleteDomainTemplate.Build()) @@ -576,6 +603,7 @@ func init() { rGetDomainDataCheck.Auth("", "", true, "") rGetDomainDataCheck.Exception("FORBIDDEN", "ResourceError", "") rGetDomainDataCheck.Exception("NOT_FOUND", "ResourceError", "") + rGetDomainDataCheck.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetDomainDataCheck.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetDomainDataCheck.Build()) @@ -591,6 +619,7 @@ func init() { rPutEntity.Exception("CONFLICT", "ResourceError", "") rPutEntity.Exception("FORBIDDEN", "ResourceError", "") rPutEntity.Exception("NOT_FOUND", "ResourceError", "") + rPutEntity.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPutEntity.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPutEntity.Build()) @@ -602,6 +631,7 @@ func init() { rGetEntity.Exception("BAD_REQUEST", "ResourceError", "") rGetEntity.Exception("FORBIDDEN", "ResourceError", "") rGetEntity.Exception("NOT_FOUND", "ResourceError", "") + rGetEntity.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetEntity.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetEntity.Build()) @@ -616,6 +646,7 @@ func init() { rDeleteEntity.Exception("CONFLICT", "ResourceError", "") rDeleteEntity.Exception("FORBIDDEN", "ResourceError", "") rDeleteEntity.Exception("NOT_FOUND", "ResourceError", "") + rDeleteEntity.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rDeleteEntity.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rDeleteEntity.Build()) @@ -625,6 +656,7 @@ func init() { rGetEntityList.Auth("", "", true, "") rGetEntityList.Exception("BAD_REQUEST", "ResourceError", "") rGetEntityList.Exception("NOT_FOUND", "ResourceError", "") + rGetEntityList.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetEntityList.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetEntityList.Build()) @@ -637,6 +669,7 @@ func init() { rGetRoleList.Exception("BAD_REQUEST", "ResourceError", "") rGetRoleList.Exception("FORBIDDEN", "ResourceError", "") rGetRoleList.Exception("NOT_FOUND", "ResourceError", "") + rGetRoleList.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetRoleList.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetRoleList.Build()) @@ -647,6 +680,7 @@ func init() { rGetRoles.Auth("", "", true, "") rGetRoles.Exception("BAD_REQUEST", "ResourceError", "") rGetRoles.Exception("NOT_FOUND", "ResourceError", "") + rGetRoles.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetRoles.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetRoles.Build()) @@ -660,6 +694,7 @@ func init() { rGetRole.Exception("BAD_REQUEST", "ResourceError", "") rGetRole.Exception("FORBIDDEN", "ResourceError", "") rGetRole.Exception("NOT_FOUND", "ResourceError", "") + rGetRole.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetRole.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetRole.Build()) @@ -675,6 +710,7 @@ func init() { rPutRole.Exception("CONFLICT", "ResourceError", "") rPutRole.Exception("FORBIDDEN", "ResourceError", "") rPutRole.Exception("NOT_FOUND", "ResourceError", "") + rPutRole.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPutRole.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPutRole.Build()) @@ -689,6 +725,7 @@ func init() { rDeleteRole.Exception("CONFLICT", "ResourceError", "") rDeleteRole.Exception("FORBIDDEN", "ResourceError", "") rDeleteRole.Exception("NOT_FOUND", "ResourceError", "") + rDeleteRole.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rDeleteRole.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rDeleteRole.Build()) @@ -701,6 +738,7 @@ func init() { rGetMembership.Exception("BAD_REQUEST", "ResourceError", "") rGetMembership.Exception("FORBIDDEN", "ResourceError", "") rGetMembership.Exception("NOT_FOUND", "ResourceError", "") + rGetMembership.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetMembership.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetMembership.Build()) @@ -717,6 +755,7 @@ func init() { rPutMembership.Exception("CONFLICT", "ResourceError", "") rPutMembership.Exception("FORBIDDEN", "ResourceError", "") rPutMembership.Exception("NOT_FOUND", "ResourceError", "") + rPutMembership.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPutMembership.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPutMembership.Build()) @@ -732,6 +771,7 @@ func init() { rDeleteMembership.Exception("CONFLICT", "ResourceError", "") rDeleteMembership.Exception("FORBIDDEN", "ResourceError", "") rDeleteMembership.Exception("NOT_FOUND", "ResourceError", "") + rDeleteMembership.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rDeleteMembership.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rDeleteMembership.Build()) @@ -745,6 +785,7 @@ func init() { rPutDefaultAdmins.Exception("BAD_REQUEST", "ResourceError", "") rPutDefaultAdmins.Exception("FORBIDDEN", "ResourceError", "") rPutDefaultAdmins.Exception("NOT_FOUND", "ResourceError", "") + rPutDefaultAdmins.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPutDefaultAdmins.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPutDefaultAdmins.Build()) @@ -757,6 +798,7 @@ func init() { rGetPolicyList.Exception("BAD_REQUEST", "ResourceError", "") rGetPolicyList.Exception("FORBIDDEN", "ResourceError", "") rGetPolicyList.Exception("NOT_FOUND", "ResourceError", "") + rGetPolicyList.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetPolicyList.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetPolicyList.Build()) @@ -767,6 +809,7 @@ func init() { rGetPolicies.Auth("", "", true, "") rGetPolicies.Exception("BAD_REQUEST", "ResourceError", "") rGetPolicies.Exception("NOT_FOUND", "ResourceError", "") + rGetPolicies.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetPolicies.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetPolicies.Build()) @@ -778,6 +821,7 @@ func init() { rGetPolicy.Exception("BAD_REQUEST", "ResourceError", "") rGetPolicy.Exception("FORBIDDEN", "ResourceError", "") rGetPolicy.Exception("NOT_FOUND", "ResourceError", "") + rGetPolicy.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetPolicy.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetPolicy.Build()) @@ -793,6 +837,7 @@ func init() { rPutPolicy.Exception("CONFLICT", "ResourceError", "") rPutPolicy.Exception("FORBIDDEN", "ResourceError", "") rPutPolicy.Exception("NOT_FOUND", "ResourceError", "") + rPutPolicy.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPutPolicy.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPutPolicy.Build()) @@ -807,6 +852,7 @@ func init() { rDeletePolicy.Exception("CONFLICT", "ResourceError", "") rDeletePolicy.Exception("FORBIDDEN", "ResourceError", "") rDeletePolicy.Exception("NOT_FOUND", "ResourceError", "") + rDeletePolicy.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rDeletePolicy.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rDeletePolicy.Build()) @@ -819,6 +865,7 @@ func init() { rGetAssertion.Exception("BAD_REQUEST", "ResourceError", "") rGetAssertion.Exception("FORBIDDEN", "ResourceError", "") rGetAssertion.Exception("NOT_FOUND", "ResourceError", "") + rGetAssertion.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetAssertion.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetAssertion.Build()) @@ -833,6 +880,7 @@ func init() { rPutAssertion.Exception("CONFLICT", "ResourceError", "") rPutAssertion.Exception("FORBIDDEN", "ResourceError", "") rPutAssertion.Exception("NOT_FOUND", "ResourceError", "") + rPutAssertion.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPutAssertion.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPutAssertion.Build()) @@ -848,6 +896,7 @@ func init() { rDeleteAssertion.Exception("CONFLICT", "ResourceError", "") rDeleteAssertion.Exception("FORBIDDEN", "ResourceError", "") rDeleteAssertion.Exception("NOT_FOUND", "ResourceError", "") + rDeleteAssertion.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rDeleteAssertion.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rDeleteAssertion.Build()) @@ -863,6 +912,7 @@ func init() { rPutServiceIdentity.Exception("CONFLICT", "ResourceError", "") rPutServiceIdentity.Exception("FORBIDDEN", "ResourceError", "") rPutServiceIdentity.Exception("NOT_FOUND", "ResourceError", "") + rPutServiceIdentity.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPutServiceIdentity.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPutServiceIdentity.Build()) @@ -874,6 +924,7 @@ func init() { rGetServiceIdentity.Exception("BAD_REQUEST", "ResourceError", "") rGetServiceIdentity.Exception("FORBIDDEN", "ResourceError", "") rGetServiceIdentity.Exception("NOT_FOUND", "ResourceError", "") + rGetServiceIdentity.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetServiceIdentity.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetServiceIdentity.Build()) @@ -888,6 +939,7 @@ func init() { rDeleteServiceIdentity.Exception("CONFLICT", "ResourceError", "") rDeleteServiceIdentity.Exception("FORBIDDEN", "ResourceError", "") rDeleteServiceIdentity.Exception("NOT_FOUND", "ResourceError", "") + rDeleteServiceIdentity.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rDeleteServiceIdentity.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rDeleteServiceIdentity.Build()) @@ -899,6 +951,7 @@ func init() { rGetServiceIdentities.Auth("", "", true, "") rGetServiceIdentities.Exception("BAD_REQUEST", "ResourceError", "") rGetServiceIdentities.Exception("NOT_FOUND", "ResourceError", "") + rGetServiceIdentities.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetServiceIdentities.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetServiceIdentities.Build()) @@ -911,6 +964,7 @@ func init() { rGetServiceIdentityList.Exception("BAD_REQUEST", "ResourceError", "") rGetServiceIdentityList.Exception("FORBIDDEN", "ResourceError", "") rGetServiceIdentityList.Exception("NOT_FOUND", "ResourceError", "") + rGetServiceIdentityList.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetServiceIdentityList.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetServiceIdentityList.Build()) @@ -923,6 +977,7 @@ func init() { rGetPublicKeyEntry.Exception("BAD_REQUEST", "ResourceError", "") rGetPublicKeyEntry.Exception("FORBIDDEN", "ResourceError", "") rGetPublicKeyEntry.Exception("NOT_FOUND", "ResourceError", "") + rGetPublicKeyEntry.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetPublicKeyEntry.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetPublicKeyEntry.Build()) @@ -939,6 +994,7 @@ func init() { rPutPublicKeyEntry.Exception("CONFLICT", "ResourceError", "") rPutPublicKeyEntry.Exception("FORBIDDEN", "ResourceError", "") rPutPublicKeyEntry.Exception("NOT_FOUND", "ResourceError", "") + rPutPublicKeyEntry.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPutPublicKeyEntry.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPutPublicKeyEntry.Build()) @@ -954,6 +1010,7 @@ func init() { rDeletePublicKeyEntry.Exception("CONFLICT", "ResourceError", "") rDeletePublicKeyEntry.Exception("FORBIDDEN", "ResourceError", "") rDeletePublicKeyEntry.Exception("NOT_FOUND", "ResourceError", "") + rDeletePublicKeyEntry.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rDeletePublicKeyEntry.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rDeletePublicKeyEntry.Build()) @@ -969,6 +1026,7 @@ func init() { rPutTenancy.Exception("CONFLICT", "ResourceError", "") rPutTenancy.Exception("FORBIDDEN", "ResourceError", "") rPutTenancy.Exception("NOT_FOUND", "ResourceError", "") + rPutTenancy.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPutTenancy.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPutTenancy.Build()) @@ -980,6 +1038,7 @@ func init() { rGetTenancy.Exception("BAD_REQUEST", "ResourceError", "") rGetTenancy.Exception("FORBIDDEN", "ResourceError", "") rGetTenancy.Exception("NOT_FOUND", "ResourceError", "") + rGetTenancy.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetTenancy.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetTenancy.Build()) @@ -994,6 +1053,7 @@ func init() { rDeleteTenancy.Exception("CONFLICT", "ResourceError", "") rDeleteTenancy.Exception("FORBIDDEN", "ResourceError", "") rDeleteTenancy.Exception("NOT_FOUND", "ResourceError", "") + rDeleteTenancy.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rDeleteTenancy.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rDeleteTenancy.Build()) @@ -1010,6 +1070,7 @@ func init() { rPutTenancyResourceGroup.Exception("CONFLICT", "ResourceError", "") rPutTenancyResourceGroup.Exception("FORBIDDEN", "ResourceError", "") rPutTenancyResourceGroup.Exception("NOT_FOUND", "ResourceError", "") + rPutTenancyResourceGroup.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPutTenancyResourceGroup.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPutTenancyResourceGroup.Build()) @@ -1025,6 +1086,7 @@ func init() { rDeleteTenancyResourceGroup.Exception("CONFLICT", "ResourceError", "") rDeleteTenancyResourceGroup.Exception("FORBIDDEN", "ResourceError", "") rDeleteTenancyResourceGroup.Exception("NOT_FOUND", "ResourceError", "") + rDeleteTenancyResourceGroup.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rDeleteTenancyResourceGroup.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rDeleteTenancyResourceGroup.Build()) @@ -1040,6 +1102,7 @@ func init() { rPutTenantRoles.Exception("CONFLICT", "ResourceError", "") rPutTenantRoles.Exception("FORBIDDEN", "ResourceError", "") rPutTenantRoles.Exception("NOT_FOUND", "ResourceError", "") + rPutTenantRoles.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPutTenantRoles.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPutTenantRoles.Build()) @@ -1052,6 +1115,7 @@ func init() { rGetTenantRoles.Exception("BAD_REQUEST", "ResourceError", "") rGetTenantRoles.Exception("FORBIDDEN", "ResourceError", "") rGetTenantRoles.Exception("NOT_FOUND", "ResourceError", "") + rGetTenantRoles.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetTenantRoles.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetTenantRoles.Build()) @@ -1067,6 +1131,7 @@ func init() { rDeleteTenantRoles.Exception("CONFLICT", "ResourceError", "") rDeleteTenantRoles.Exception("FORBIDDEN", "ResourceError", "") rDeleteTenantRoles.Exception("NOT_FOUND", "ResourceError", "") + rDeleteTenantRoles.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rDeleteTenantRoles.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rDeleteTenantRoles.Build()) @@ -1083,6 +1148,7 @@ func init() { rPutTenantResourceGroupRoles.Exception("CONFLICT", "ResourceError", "") rPutTenantResourceGroupRoles.Exception("FORBIDDEN", "ResourceError", "") rPutTenantResourceGroupRoles.Exception("NOT_FOUND", "ResourceError", "") + rPutTenantResourceGroupRoles.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPutTenantResourceGroupRoles.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPutTenantResourceGroupRoles.Build()) @@ -1096,6 +1162,7 @@ func init() { rGetTenantResourceGroupRoles.Exception("BAD_REQUEST", "ResourceError", "") rGetTenantResourceGroupRoles.Exception("FORBIDDEN", "ResourceError", "") rGetTenantResourceGroupRoles.Exception("NOT_FOUND", "ResourceError", "") + rGetTenantResourceGroupRoles.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetTenantResourceGroupRoles.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetTenantResourceGroupRoles.Build()) @@ -1112,6 +1179,7 @@ func init() { rDeleteTenantResourceGroupRoles.Exception("CONFLICT", "ResourceError", "") rDeleteTenantResourceGroupRoles.Exception("FORBIDDEN", "ResourceError", "") rDeleteTenantResourceGroupRoles.Exception("NOT_FOUND", "ResourceError", "") + rDeleteTenantResourceGroupRoles.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rDeleteTenantResourceGroupRoles.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rDeleteTenantResourceGroupRoles.Build()) @@ -1128,6 +1196,7 @@ func init() { rPutProviderResourceGroupRoles.Exception("CONFLICT", "ResourceError", "") rPutProviderResourceGroupRoles.Exception("FORBIDDEN", "ResourceError", "") rPutProviderResourceGroupRoles.Exception("NOT_FOUND", "ResourceError", "") + rPutProviderResourceGroupRoles.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPutProviderResourceGroupRoles.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPutProviderResourceGroupRoles.Build()) @@ -1141,6 +1210,7 @@ func init() { rGetProviderResourceGroupRoles.Exception("BAD_REQUEST", "ResourceError", "") rGetProviderResourceGroupRoles.Exception("FORBIDDEN", "ResourceError", "") rGetProviderResourceGroupRoles.Exception("NOT_FOUND", "ResourceError", "") + rGetProviderResourceGroupRoles.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetProviderResourceGroupRoles.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetProviderResourceGroupRoles.Build()) @@ -1157,6 +1227,7 @@ func init() { rDeleteProviderResourceGroupRoles.Exception("CONFLICT", "ResourceError", "") rDeleteProviderResourceGroupRoles.Exception("FORBIDDEN", "ResourceError", "") rDeleteProviderResourceGroupRoles.Exception("NOT_FOUND", "ResourceError", "") + rDeleteProviderResourceGroupRoles.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rDeleteProviderResourceGroupRoles.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rDeleteProviderResourceGroupRoles.Build()) @@ -1170,6 +1241,7 @@ func init() { rGetAccess.Exception("BAD_REQUEST", "ResourceError", "") rGetAccess.Exception("FORBIDDEN", "ResourceError", "") rGetAccess.Exception("NOT_FOUND", "ResourceError", "") + rGetAccess.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetAccess.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetAccess.Build()) @@ -1184,6 +1256,7 @@ func init() { rGetAccessExt.Exception("BAD_REQUEST", "ResourceError", "") rGetAccessExt.Exception("FORBIDDEN", "ResourceError", "") rGetAccessExt.Exception("NOT_FOUND", "ResourceError", "") + rGetAccessExt.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetAccessExt.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetAccessExt.Build()) @@ -1195,6 +1268,7 @@ func init() { rGetResourceAccessList.Exception("BAD_REQUEST", "ResourceError", "") rGetResourceAccessList.Exception("FORBIDDEN", "ResourceError", "") rGetResourceAccessList.Exception("NOT_FOUND", "ResourceError", "") + rGetResourceAccessList.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetResourceAccessList.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetResourceAccessList.Build()) @@ -1207,6 +1281,7 @@ func init() { rGetSignedDomains.Auth("", "", true, "") rGetSignedDomains.Exception("FORBIDDEN", "ResourceError", "") rGetSignedDomains.Exception("NOT_FOUND", "ResourceError", "") + rGetSignedDomains.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetSignedDomains.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetSignedDomains.Build()) @@ -1217,6 +1292,7 @@ func init() { rGetUserToken.Input("header", "Bool", false, "header", "", true, false, "include Authorization header name in response") rGetUserToken.Auth("", "", true, "") rGetUserToken.Exception("FORBIDDEN", "ResourceError", "") + rGetUserToken.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetUserToken.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetUserToken.Build()) @@ -1225,6 +1301,7 @@ func init() { rOptionsUserToken.Input("userName", "SimpleName", true, "", "", false, nil, "name of the user") rOptionsUserToken.Input("serviceNames", "String", false, "services", "", true, nil, "comma separated list of on-behalf-of service names") rOptionsUserToken.Exception("BAD_REQUEST", "ResourceError", "") + rOptionsUserToken.Exception("TOO_MANY_REQUESTS", "ResourceError", "") sb.AddResource(rOptionsUserToken.Build()) rGetServicePrincipal := rdl.NewResourceBuilder("ServicePrincipal", "GET", "/principal") @@ -1232,12 +1309,14 @@ func init() { rGetServicePrincipal.Auth("", "", true, "") rGetServicePrincipal.Exception("BAD_REQUEST", "ResourceError", "") rGetServicePrincipal.Exception("FORBIDDEN", "ResourceError", "") + rGetServicePrincipal.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetServicePrincipal.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetServicePrincipal.Build()) rGetServerTemplateList := rdl.NewResourceBuilder("ServerTemplateList", "GET", "/template") rGetServerTemplateList.Comment("Get the list of solution templates defined in the server") rGetServerTemplateList.Auth("", "", true, "") + rGetServerTemplateList.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetServerTemplateList.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetServerTemplateList.Build()) @@ -1247,12 +1326,14 @@ func init() { rGetTemplate.Auth("", "", true, "") rGetTemplate.Exception("BAD_REQUEST", "ResourceError", "") rGetTemplate.Exception("NOT_FOUND", "ResourceError", "") + rGetTemplate.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetTemplate.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetTemplate.Build()) rGetUserList := rdl.NewResourceBuilder("UserList", "GET", "/user") rGetUserList.Comment("Enumerate users that are registered as principals in the system This will return only the principals with \".\" prefix") rGetUserList.Auth("", "", true, "") + rGetUserList.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rGetUserList.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rGetUserList.Build()) @@ -1267,6 +1348,7 @@ func init() { rPutUserMeta.Exception("CONFLICT", "ResourceError", "") rPutUserMeta.Exception("FORBIDDEN", "ResourceError", "") rPutUserMeta.Exception("NOT_FOUND", "ResourceError", "") + rPutUserMeta.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rPutUserMeta.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rPutUserMeta.Build()) @@ -1280,9 +1362,45 @@ func init() { rDeleteUser.Exception("CONFLICT", "ResourceError", "") rDeleteUser.Exception("FORBIDDEN", "ResourceError", "") rDeleteUser.Exception("NOT_FOUND", "ResourceError", "") + rDeleteUser.Exception("TOO_MANY_REQUESTS", "ResourceError", "") rDeleteUser.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(rDeleteUser.Build()) + rGetQuota := rdl.NewResourceBuilder("Quota", "GET", "/domain/{name}/quota") + rGetQuota.Comment("Retrieve the quota object defined for the domain") + rGetQuota.Input("name", "DomainName", true, "", "", false, nil, "name of the domain") + rGetQuota.Auth("", "", true, "") + rGetQuota.Exception("BAD_REQUEST", "ResourceError", "") + rGetQuota.Exception("NOT_FOUND", "ResourceError", "") + sb.AddResource(rGetQuota.Build()) + + rPutQuota := rdl.NewResourceBuilder("Quota", "PUT", "/domain/{name}/quota") + rPutQuota.Comment("Update the specified domain's quota object") + rPutQuota.Input("name", "DomainName", true, "", "", false, nil, "name of the domain") + rPutQuota.Input("auditRef", "String", false, "", "Y-Audit-Ref", false, nil, "Audit reference") + rPutQuota.Input("quota", "Quota", false, "", "", false, nil, "Quota object with limits for the domain") + rPutQuota.Auth("update", "sys.auth:quota", false, "") + rPutQuota.Expected("NO_CONTENT") + rPutQuota.Exception("BAD_REQUEST", "ResourceError", "") + rPutQuota.Exception("CONFLICT", "ResourceError", "") + rPutQuota.Exception("FORBIDDEN", "ResourceError", "") + rPutQuota.Exception("NOT_FOUND", "ResourceError", "") + rPutQuota.Exception("UNAUTHORIZED", "ResourceError", "") + sb.AddResource(rPutQuota.Build()) + + rDeleteQuota := rdl.NewResourceBuilder("Quota", "DELETE", "/domain/{name}/quota") + rDeleteQuota.Comment("Delete the specified domain's quota") + rDeleteQuota.Input("name", "DomainName", true, "", "", false, nil, "name of the domain") + rDeleteQuota.Input("auditRef", "String", false, "", "Y-Audit-Ref", false, nil, "Audit reference") + rDeleteQuota.Auth("update", "sys.auth:quota", false, "") + rDeleteQuota.Expected("NO_CONTENT") + rDeleteQuota.Exception("BAD_REQUEST", "ResourceError", "") + rDeleteQuota.Exception("CONFLICT", "ResourceError", "") + rDeleteQuota.Exception("FORBIDDEN", "ResourceError", "") + rDeleteQuota.Exception("NOT_FOUND", "ResourceError", "") + rDeleteQuota.Exception("UNAUTHORIZED", "ResourceError", "") + sb.AddResource(rDeleteQuota.Build()) + schema = sb.Build() } diff --git a/clients/java/zms/src/main/java/com/yahoo/athenz/zms/ResourceException.java b/clients/java/zms/src/main/java/com/yahoo/athenz/zms/ResourceException.java index 7800dfaea6b..76478d6e64c 100644 --- a/clients/java/zms/src/main/java/com/yahoo/athenz/zms/ResourceException.java +++ b/clients/java/zms/src/main/java/com/yahoo/athenz/zms/ResourceException.java @@ -21,10 +21,13 @@ public class ResourceException extends RuntimeException { public final static int GONE = 410; public final static int PRECONDITION_FAILED = 412; public final static int UNSUPPORTED_MEDIA_TYPE = 415; + public final static int PRECONDITION_REQUIRED = 428; + public final static int TOO_MANY_REQUESTS = 429; + public final static int REQUEST_HEADER_FIELDS_TOO_LARGE = 431; public final static int INTERNAL_SERVER_ERROR = 500; public final static int NOT_IMPLEMENTED = 501; - public final static int SERVICE_UNAVAILABLE = 503; + public final static int NETWORK_AUTHENTICATION_REQUIRED = 511; public static String codeToString(int code) { switch (code) { @@ -45,8 +48,13 @@ public static String codeToString(int code) { case GONE: return "Gone"; case PRECONDITION_FAILED: return "Precondition Failed"; case UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type"; + case PRECONDITION_REQUIRED: return "Precondition Required"; + case TOO_MANY_REQUESTS: return "Too Many Requests"; + case REQUEST_HEADER_FIELDS_TOO_LARGE: return "Request Header Fields Too Large"; case INTERNAL_SERVER_ERROR: return "Internal Server Error"; case NOT_IMPLEMENTED: return "Not Implemented"; + case SERVICE_UNAVAILABLE: return "Service Unavailable"; + case NETWORK_AUTHENTICATION_REQUIRED: return "Network Authentication Required"; default: return "" + code; } } diff --git a/clients/java/zms/src/main/java/com/yahoo/athenz/zms/ZMSClient.java b/clients/java/zms/src/main/java/com/yahoo/athenz/zms/ZMSClient.java index be38930f3d9..60d82ca6a45 100644 --- a/clients/java/zms/src/main/java/com/yahoo/athenz/zms/ZMSClient.java +++ b/clients/java/zms/src/main/java/com/yahoo/athenz/zms/ZMSClient.java @@ -341,7 +341,8 @@ public String generateServiceIdentityName(String domain, String service) { /** * Retrieve the specified domain object * @param domain name of the domain to be retrieved - * @return Domain object or ZMSClientException will be thrown in case of failure + * @return Domain object + * @throws ZMSClientException in case of failure */ public Domain getDomain(String domain) { updatePrincipal(); @@ -356,7 +357,8 @@ public Domain getDomain(String domain) { /** * Retrieve the list of domains provisioned on the ZMS Server - * @return list of Domains or ZMSClientException will be thrown in case of failure + * @return list of Domains + * @throws ZMSClientException in case of failure */ public DomainList getDomainList() { return getDomainList(null, null, null, null, null, null, null); @@ -376,7 +378,8 @@ public DomainList getDomainList() { * is specified all other optional attributes are ignored since there must be * only one domain matching the specified product id. * @param modifiedSince return domains only modified since this date - * @return list of domain names or ZMSClientException will be thrown in case of failure + * @return list of domain names + * @throws ZMSClientException in case of failure */ public DomainList getDomainList(Integer limit, String skip, String prefix, Integer depth, String account, Integer productId, Date modifiedSince) { @@ -400,7 +403,8 @@ public DomainList getDomainList(Integer limit, String skip, String prefix, Integ * filters based on the specified arguments * @param roleMember name of the principal * @param roleName name of the role where the principal is a member of - * @return list of domain names or ZMSClientException will be thrown in case of failure + * @return list of domain names + * @throws ZMSClientException in case of failure */ public DomainList getDomainList(String roleMember, String roleName) { updatePrincipal(); @@ -419,7 +423,8 @@ public DomainList getDomainList(String roleMember, String roleName) { * object configured on the server (not just some of the attributes). * @param auditRef string containing audit specification or ticket number * @param detail TopLevelDomain object to be created in ZMS - * @return created Domain object or ZMSClientException will be thrown in case of failure + * @return created Domain object + * @throws ZMSClientException in case of failure */ public Domain postTopLevelDomain(String auditRef, TopLevelDomain detail) { updatePrincipal(); @@ -440,7 +445,8 @@ public Domain postTopLevelDomain(String auditRef, TopLevelDomain detail) { * @param parent name of the parent domain * @param auditRef string containing audit specification or ticket number * @param detail SubDomain object to be created in ZMS - * @return created Domain object or ZMSClientException will be thrown in case of failure + * @return created Domain object + * @throws ZMSClientException in case of failure */ public Domain postSubDomain(String parent, String auditRef, SubDomain detail) { updatePrincipal(); @@ -458,7 +464,8 @@ public Domain postSubDomain(String parent, String auditRef, SubDomain detail) { * @param name domain to be created, this is the <userid> * @param auditRef string containing audit specification or ticket number * @param detail UserDomain object to be created in ZMS - * @return created Domain object or ZMSClientException will be thrown in case of failure + * @return created Domain object + * @throws ZMSClientException in case of failure */ public Domain postUserDomain(String name, String auditRef, UserDomain detail) { updatePrincipal(); @@ -475,6 +482,7 @@ public Domain postUserDomain(String name, String auditRef, UserDomain detail) { * Delete a top level domain * @param name domain name to be deleted from ZMS * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure */ public void deleteTopLevelDomain(String name, String auditRef) { updatePrincipal(); @@ -492,6 +500,7 @@ public void deleteTopLevelDomain(String name, String auditRef) { * @param parent name of the parent domain * @param name sub-domain to be deleted * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure */ public void deleteSubDomain(String parent, String name, String auditRef) { updatePrincipal(); @@ -542,6 +551,7 @@ public void putDomainMeta(String name, String auditRef, DomainMeta detail) { * @param name user name (without the domain part) * @param auditRef string containing audit specification or ticket number * @param detail meta parameters to be set on the domain + * @throws ZMSClientException in case of failure */ public void putUserMeta(String name, String auditRef, UserMeta detail) { try { @@ -556,7 +566,8 @@ public void putUserMeta(String name, String auditRef, UserMeta detail) { /** * Retrieve the list of roles defined for the specified domain * @param domainName name of the domain - * @return list of role names or ZMSClientException will be thrown in case of failure + * @return list of role names + * @throws ZMSClientException in case of failure */ public RoleList getRoleList(String domainName) { updatePrincipal(); @@ -575,7 +586,8 @@ public RoleList getRoleList(String domainName) { * @param domainName name of the domain * @param limit number of roles to return * @param skip exclude all the roles including the specified one from the return set - * @return list of role names or ZMSClientException will be thrown in case of failure + * @return list of role names + * @throws ZMSClientException in case of failure */ public RoleList getRoleList(String domainName, Integer limit, String skip) { updatePrincipal(); @@ -593,7 +605,8 @@ public RoleList getRoleList(String domainName, Integer limit, String skip) { * will contain their attributes and, if specified, the list of members. * @param domainName name of the domain * @param members include all members for group roles as well - * @return list of roles or ZMSClientException will be thrown in case of failure + * @return list of roles + * @throws ZMSClientException in case of failure */ public Roles getRoles(String domainName, Boolean members) { updatePrincipal(); @@ -610,7 +623,8 @@ public Roles getRoles(String domainName, Boolean members) { * Retrieve the specified role * @param domainName name of the domain * @param roleName name of the role - * @return role object or ZMSClientException will be thrown in case of failure + * @return role object + * @throws ZMSClientException in case of failure */ public Role getRole(String domainName, String roleName) { return getRole(domainName, roleName, false, false); @@ -621,7 +635,8 @@ public Role getRole(String domainName, String roleName) { * @param domainName name of the domain * @param roleName name of the role * @param auditLog include audit log for the role changes in the response - * @return role object or ZMSClientException will be thrown in case of failure + * @return role object + * @throws ZMSClientException in case of failure */ public Role getRole(String domainName, String roleName, boolean auditLog) { return getRole(domainName, roleName, auditLog, false); @@ -636,7 +651,8 @@ public Role getRole(String domainName, String roleName, boolean auditLog) { * @param expand if the requested role is a delegated/trust role, this flag * will instruct the ZMS server to automatically retrieve the members of the * role from the delegated domain and return as part of the role object - * @return role object or ZMSClientException will be thrown in case of failure + * @return role object + * @throws ZMSClientException in case of failure */ public Role getRole(String domainName, String roleName, boolean auditLog, boolean expand) { updatePrincipal(); @@ -657,6 +673,7 @@ public Role getRole(String domainName, String roleName, boolean auditLog, boolea * @param roleName name of the role * @param auditRef string containing audit specification or ticket number * @param role role object to be added to the domain + * @throws ZMSClientException in case of failure */ public void putRole(String domainName, String roleName, String auditRef, Role role) { updatePrincipal(); @@ -674,6 +691,7 @@ public void putRole(String domainName, String roleName, String auditRef, Role ro * @param domainName name of the domain * @param roleName name of the role * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure */ public void deleteRole(String domainName, String roleName, String auditRef) { updatePrincipal(); @@ -692,7 +710,8 @@ public void deleteRole(String domainName, String roleName, String auditRef) { * @param domainName name of the domain * @param roleName name of the role * @param memberName name of the member - * @return Membership object or ZMSClientException will be thrown in case of failure + * @return Membership object + * @throws ZMSClientException in case of failure */ public Membership getMembership(String domainName, String roleName, String memberName) { updatePrincipal(); @@ -711,6 +730,7 @@ public Membership getMembership(String domainName, String roleName, String membe * @param roleName name of the role * @param memberName name of the member to be added * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure */ public void putMembership(String domainName, String roleName, String memberName, String auditRef) { putMembership(domainName, roleName, memberName, null, auditRef); @@ -723,6 +743,7 @@ public void putMembership(String domainName, String roleName, String memberName, * @param memberName name of the member to be added * @param expiration timestamp when this membership will expire (optional) * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure */ public void putMembership(String domainName, String roleName, String memberName, Timestamp expiration, String auditRef) { @@ -745,6 +766,7 @@ public void putMembership(String domainName, String roleName, String memberName, * @param roleName name of the role * @param memberName name of the member to be removed * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure */ public void deleteMembership(String domainName, String roleName, String memberName, String auditRef) { updatePrincipal(); @@ -759,7 +781,8 @@ public void deleteMembership(String domainName, String roleName, String memberNa /** * Get list of users defined in the system - * @return list of user names or ZMSClientException will be thrown in case of failure + * @return list of user names + * @throws ZMSClientException in case of failure */ public UserList getUserList() { updatePrincipal(); @@ -779,6 +802,7 @@ public UserList getUserList() { * from the Athens sys.auth domain (delete action on resource user). * @param name name of the user * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure */ public void deleteUser(String name, String auditRef) { updatePrincipal(); @@ -796,7 +820,8 @@ public void deleteUser(String name, String auditRef) { * will contain their attributes and, if specified, the list of assertions. * @param domainName name of the domain * @param assertions include all assertion for policies as well - * @return list of policies or ZMSClientException will be thrown in case of failure + * @return list of policies + * @throws ZMSClientException in case of failure */ public Policies getPolicies(String domainName, Boolean assertions) { updatePrincipal(); @@ -812,7 +837,8 @@ public Policies getPolicies(String domainName, Boolean assertions) { /** * Get list of policies defined in the specified domain * @param domainName name of the domain - * @return list of policy names or ZMSClientException will be thrown in case of failure + * @return list of policy names + * @throws ZMSClientException in case of failure */ public PolicyList getPolicyList(String domainName) { updatePrincipal(); @@ -831,7 +857,8 @@ public PolicyList getPolicyList(String domainName) { * @param domainName name of the domain * @param limit number of policies to return * @param skip exclude all the policies including the specified one from the return set - * @return list of policy names or ZMSClientException will be thrown in case of failure + * @return list of policy names + * @throws ZMSClientException in case of failure */ public PolicyList getPolicyList(String domainName, Integer limit, String skip) { updatePrincipal(); @@ -849,7 +876,8 @@ public PolicyList getPolicyList(String domainName, Integer limit, String skip) { * @param domainName name of the domain * @param policyName name of the policy * @param assertionId the id of the assertion to be retrieved - * @return Assertion object or ZMSClientException will be thrown in case of failure + * @return Assertion object + * @throws ZMSClientException in case of failure */ public Assertion getAssertion(String domainName, String policyName, Long assertionId) { updatePrincipal(); @@ -869,6 +897,7 @@ public Assertion getAssertion(String domainName, String policyName, Long asserti * @param auditRef string containing audit specification or ticket number * @param assertion Assertion object to be added to the policy * @return updated assertion object that includes the server assigned id + * @throws ZMSClientException in case of failure */ public Assertion putAssertion(String domainName, String policyName, String auditRef, Assertion assertion) { updatePrincipal(); @@ -887,6 +916,7 @@ public Assertion putAssertion(String domainName, String policyName, String audit * @param policyName name of the policy * @param assertionId the id of the assertion to be deleted * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure */ public void deleteAssertion(String domainName, String policyName, Long assertionId, String auditRef) { updatePrincipal(); @@ -903,7 +933,8 @@ public void deleteAssertion(String domainName, String policyName, Long assertion * Return the specified policy object * @param domainName name of the domain * @param policyName name of the policy to be retrieved - * @return Policy object or ZMSClientException will be thrown in case of failure + * @return Policy object + * @throws ZMSClientException in case of failure */ public Policy getPolicy(String domainName, String policyName) { updatePrincipal(); @@ -924,6 +955,7 @@ public Policy getPolicy(String domainName, String policyName) { * @param policyName name of the policy * @param auditRef string containing audit specification or ticket number * @param policy Policy object with details + * @throws ZMSClientException in case of failure */ public void putPolicy(String domainName, String policyName, String auditRef, Policy policy) { updatePrincipal(); @@ -941,6 +973,7 @@ public void putPolicy(String domainName, String policyName, String auditRef, Pol * @param domainName name of the domain * @param policyName name of the policy to be deleted * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure */ public void deletePolicy(String domainName, String policyName, String auditRef) { updatePrincipal(); @@ -961,6 +994,7 @@ public void deletePolicy(String domainName, String policyName, String auditRef) * @param serviceName name of the service * @param auditRef string containing audit specification or ticket number * @param service ServiceIdentity object with all service details + * @throws ZMSClientException in case of failure */ public void putServiceIdentity(String domainName, String serviceName, String auditRef, ServiceIdentity service) { @@ -978,7 +1012,8 @@ public void putServiceIdentity(String domainName, String serviceName, * Retrieve the specified service object from a domain * @param domainName name of the domain * @param serviceName name of the service to be retrieved - * @return ServiceIdentity object or ZMSClientException will be thrown in case of failure + * @return ServiceIdentity object + * @throws ZMSClientException in case of failure */ public ServiceIdentity getServiceIdentity(String domainName, String serviceName) { updatePrincipal(); @@ -996,6 +1031,7 @@ public ServiceIdentity getServiceIdentity(String domainName, String serviceName) * @param domainName name of the domain * @param serviceName name of the service to be deleted * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure */ public void deleteServiceIdentity(String domainName, String serviceName, String auditRef) { updatePrincipal(); @@ -1014,7 +1050,8 @@ public void deleteServiceIdentity(String domainName, String serviceName, String * @param domainName name of the domain * @param publicKeys include all public keys for services as well * @param hosts include all configured hosts for services as well - * @return list of services or ZMSClientException will be thrown in case of failure + * @return list of services + * @throws ZMSClientException in case of failure */ public ServiceIdentities getServiceIdentities(String domainName, Boolean publicKeys, Boolean hosts) { updatePrincipal(); @@ -1030,7 +1067,8 @@ public ServiceIdentities getServiceIdentities(String domainName, Boolean publicK /** * Retrieve the full list of services defined in a domain * @param domainName name of the domain - * @return list of all service names or ZMSClientException will be thrown in case of failure + * @return list of all service names + * @throws ZMSClientException in case of failure */ public ServiceIdentityList getServiceIdentityList(String domainName) { updatePrincipal(); @@ -1049,7 +1087,8 @@ public ServiceIdentityList getServiceIdentityList(String domainName) { * @param domainName name of the domain * @param limit number of services to return * @param skip exclude all the services including the specified one from the return set - * @return list of service names or ZMSClientException will be thrown in case of failure + * @return list of service names + * @throws ZMSClientException in case of failure */ public ServiceIdentityList getServiceIdentityList(String domainName, Integer limit, String skip) { updatePrincipal(); @@ -1067,7 +1106,8 @@ public ServiceIdentityList getServiceIdentityList(String domainName, Integer lim * @param domainName name of the domain * @param serviceName name of the service * @param keyId the identifier of the public key to be retrieved - * @return PublicKeyEntry object or ZMSClientException will be thrown in case of failure + * @return PublicKeyEntry object + * @throws ZMSClientException in case of failure */ public PublicKeyEntry getPublicKeyEntry(String domainName, String serviceName, String keyId) { updatePrincipal(); @@ -1087,6 +1127,7 @@ public PublicKeyEntry getPublicKeyEntry(String domainName, String serviceName, S * @param keyId the identifier of the public key to be updated * @param auditRef string containing audit specification or ticket number * @param publicKeyEntry that contains the public key details + * @throws ZMSClientException in case of failure */ public void putPublicKeyEntry(String domainName, String serviceName, String keyId, String auditRef, PublicKeyEntry publicKeyEntry) { @@ -1107,6 +1148,7 @@ public void putPublicKeyEntry(String domainName, String serviceName, String keyI * @param serviceName name of the service * @param keyId the identifier of the public key to be deleted * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure */ public void deletePublicKeyEntry(String domainName, String serviceName, String keyId, String auditRef) { updatePrincipal(); @@ -1125,6 +1167,7 @@ public void deletePublicKeyEntry(String domainName, String serviceName, String k * @param entityName name of the entity * @param auditRef string containing audit specification or ticket number * @param entity entity object with details + * @throws ZMSClientException in case of failure */ public void putEntity(String domainName, String entityName, String auditRef, Entity entity) { updatePrincipal(); @@ -1141,7 +1184,8 @@ public void putEntity(String domainName, String entityName, String auditRef, Ent * Retrieve the specified entity from the ZMS Server * @param domainName name of the domain * @param entityName name of the entity - * @return Entity object with details or ZMSClientException will be thrown in case of failure + * @return Entity object with details + * @throws ZMSClientException in case of failure */ public Entity getEntity(String domainName, String entityName) { updatePrincipal(); @@ -1159,6 +1203,7 @@ public Entity getEntity(String domainName, String entityName) { * @param domainName name of the domain * @param entityName name of the entity * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure */ public void deleteEntity(String domainName, String entityName, String auditRef) { updatePrincipal(); @@ -1174,7 +1219,8 @@ public void deleteEntity(String domainName, String entityName, String auditRef) /** * Retrieve the list of entities defined for the specified domain * @param domainName name of the domain - * @return list of entity names or ZMSClientException will be thrown in case of failure + * @return list of entity names + * @throws ZMSClientException in case of failure */ public EntityList getEntityList(String domainName) { updatePrincipal(); @@ -1195,6 +1241,7 @@ public EntityList getEntityList(String domainName) { * format: provider-domain-name.provider-service-name, ex: "sports.storage" * @param auditRef string containing audit specification or ticket number * @param tenant Tenancy object with tenant details + * @throws ZMSClientException in case of failure */ public void putTenancy(String tenantDomain, String providerService, String auditRef, Tenancy tenant) { updatePrincipal(); @@ -1212,7 +1259,8 @@ public void putTenancy(String tenantDomain, String providerService, String audit * @param tenantDomain name of the tenant domain * @param providerService name of the provider service, * format: provider-domain-name.provider-service-name, ex: "sports.storage" - * @return Tenancy object or ZMSClientException will be thrown in case of failure + * @return Tenancy object + * @throws ZMSClientException in case of failure */ public Tenancy getTenancy(String tenantDomain, String providerService) { updatePrincipal(); @@ -1231,6 +1279,7 @@ public Tenancy getTenancy(String tenantDomain, String providerService) { * @param providerService name of the provider service, * format: provider-domain-name.provider-service-name, ex: "sports.storage" * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure */ public void deleteTenancy(String tenantDomain, String providerService, String auditRef) { updatePrincipal(); @@ -1252,6 +1301,7 @@ public void deleteTenancy(String tenantDomain, String providerService, String au * @param resourceGroup name of the resource group * @param auditRef string containing audit specification or ticket number * @param resourceGroupData TenancyResourceGroup object with tenant's resource group details + * @throws ZMSClientException in case of failure */ public void putTenancyResourceGroup(String tenantDomain, String providerService, String resourceGroup, String auditRef, TenancyResourceGroup resourceGroupData) { @@ -1272,6 +1322,7 @@ public void putTenancyResourceGroup(String tenantDomain, String providerService, * provider-domain-name.provider-service-name, ex: "sports.storage" * @param resourceGroup name of the resource group * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure */ public void deleteTenancyResourceGroup(String tenantDomain, String providerService, String resourceGroup, String auditRef) { @@ -1292,6 +1343,7 @@ public void deleteTenancyResourceGroup(String tenantDomain, String providerServi * @param tenantDomain name of the tenant's domain * @param auditRef string containing audit specification or ticket number * @param tenantRoles Tenant roles + * @throws ZMSClientException in case of failure */ public void putTenantRoles(String providerDomain, String providerServiceName, String tenantDomain, String auditRef, TenantRoles tenantRoles) { @@ -1313,6 +1365,7 @@ public void putTenantRoles(String providerDomain, String providerServiceName, * @param resourceGroup name of the resource group * @param auditRef string containing audit specification or ticket number * @param tenantRoles Tenant roles + * @throws ZMSClientException in case of failure */ public void putTenantResourceGroupRoles(String providerDomain, String providerServiceName, String tenantDomain, String resourceGroup, String auditRef, TenantResourceGroupRoles tenantRoles) { @@ -1332,7 +1385,8 @@ public void putTenantResourceGroupRoles(String providerDomain, String providerSe * @param providerDomain name of the provider domain * @param providerServiceName name of the provider service * @param tenantDomain name of the tenant's domain - * @return list of tenant roles or ZMSClientException will be thrown in case of failure + * @return list of tenant roles + * @throws ZMSClientException in case of failure */ public TenantRoles getTenantRoles(String providerDomain, String providerServiceName, String tenantDomain) { updatePrincipal(); @@ -1351,7 +1405,8 @@ public TenantRoles getTenantRoles(String providerDomain, String providerServiceN * @param providerServiceName name of the provider service * @param tenantDomain name of the tenant's domain * @param resourceGroup name of the resource group - * @return list of tenant roles or ZMSClientException will be thrown in case of failure + * @return list of tenant roles + * @throws ZMSClientException in case of failure */ public TenantResourceGroupRoles getTenantResourceGroupRoles(String providerDomain, String providerServiceName, String tenantDomain, String resourceGroup) { @@ -1372,6 +1427,7 @@ public TenantResourceGroupRoles getTenantResourceGroupRoles(String providerDomai * @param providerServiceName name of the provider service * @param tenantDomain name of tenant's domain * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure */ public void deleteTenantRoles(String providerDomain, String providerServiceName, String tenantDomain, String auditRef) { @@ -1392,6 +1448,7 @@ public void deleteTenantRoles(String providerDomain, String providerServiceName, * @param tenantDomain name of tenant's domain * @param resourceGroup name of the resource group * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure */ public void deleteTenantResourceGroupRoles(String providerDomain, String providerServiceName, String tenantDomain, String resourceGroup, String auditRef) { @@ -1414,6 +1471,7 @@ public void deleteTenantResourceGroupRoles(String providerDomain, String provide * @param trustDomain (optional) if the access checks involves cross domain check only * check the specified trusted domain and ignore all others * @return Access object indicating whether or not the request will be granted or not + * @throws ZMSClientException in case of failure */ public Access getAccess(String action, String resource, String trustDomain) { return getAccess(action, resource, trustDomain, null); @@ -1428,6 +1486,7 @@ public Access getAccess(String action, String resource, String trustDomain) { * check the specified trusted domain and ignore all others * @param principal (optional) carry out the access check for specified principal * @return Access object indicating whether or not the request will be granted or not + * @throws ZMSClientException in case of failure */ public Access getAccess(String action, String resource, String trustDomain, String principal) { try { @@ -1449,6 +1508,7 @@ public Access getAccess(String action, String resource, String trustDomain, Stri * check the specified trusted domain and ignore all others * @param principal (optional) carry out the access check for specified principal * @return Access object indicating whether or not the request will be granted or not + * @throws ZMSClientException in case of failure */ public Access getAccessExt(String action, String resource, String trustDomain, String principal) { try { @@ -1478,6 +1538,7 @@ public Access getAccessExt(String action, String resource, String trustDomain, S * contain a single value timestamp String to be used * with subsequent call as matchingTag to this API * @return list of domains signed by ZMS Server + * @throws ZMSClientException in case of failure */ public SignedDomains getSignedDomains(String domainName, String metaOnly, String matchingTag, Map> responseHeaders) { @@ -1501,6 +1562,7 @@ public SignedDomains getSignedDomains(String domainName, String metaOnly, String * the user name from the credentials and is optional. The caller can just pass * the string "_self_" as the userName to bypass this optional check. * @return ZMS generated User Token + * @throws ZMSClientException in case of failure */ public UserToken getUserToken(String userName) { return getUserToken(userName, null, null); @@ -1514,6 +1576,7 @@ public UserToken getUserToken(String userName) { * @param serviceNames comma separated list of authorized service names * @param header boolean flag whether or not return authority header name * @return ZMS generated User Token + * @throws ZMSClientException in case of failure */ public UserToken getUserToken(String userName, String serviceNames, Boolean header) { try { @@ -1532,6 +1595,7 @@ public UserToken getUserToken(String userName, String serviceNames, Boolean head * @param userName name of the user * @param serviceNames comma separated list of authorized service names * @return ZMS generated User Token + * @throws ZMSClientException in case of failure */ public UserToken getUserToken(String userName, String serviceNames) { try { @@ -1551,6 +1615,7 @@ public UserToken getUserToken(String userName, String serviceNames) { * @param domainName - name of the domain to add default administrators to * @param auditRef - string containing audit specification or ticket number * @param defaultAdmins - list of names to be added as default administrators + * @throws ZMSClientException in case of failure */ public void putDefaultAdmins(String domainName, String auditRef, DefaultAdmins defaultAdmins) { updatePrincipal(); @@ -1568,7 +1633,7 @@ public void putDefaultAdmins(String domainName, String auditRef, DefaultAdmins d * and if the token is valid, it will return a Principal object. * @param serviceToken token to be validated. * @return Principal object if the token is successfully validated or - * ZMSClientException will be thrown in case of failure + * @throws ZMSClientException in case of failure */ public Principal getPrincipal(String serviceToken) { return getPrincipal(serviceToken, PRINCIPAL_AUTHORITY.getHeader()); @@ -1580,7 +1645,7 @@ public Principal getPrincipal(String serviceToken) { * @param serviceToken token to be validated. * @param tokenHeader name of the authorization header for the token * @return Principal object if the token is successfully validated or - * ZMSClientException will be thrown in case of failure + * @throws ZMSClientException in case of failure */ public Principal getPrincipal(String serviceToken, String tokenHeader) { @@ -1651,6 +1716,7 @@ public Principal getPrincipal(String serviceToken, String tokenHeader) { * @param resourceGroup name of the resource group * @param auditRef string containing audit specification or ticket number * @param providerRoles Provider roles + * @throws ZMSClientException in case of failure */ public void putProviderResourceGroupRoles(String tenantDomain, String providerDomain, String providerServiceName, String resourceGroup, String auditRef, @@ -1676,6 +1742,7 @@ public void putProviderResourceGroupRoles(String tenantDomain, String providerDo * @param providerServiceName name of the provider service * @param resourceGroup name of the resource group * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure */ public void deleteProviderResourceGroupRoles(String tenantDomain, String providerDomain, String providerServiceName, String resourceGroup, String auditRef) { @@ -1696,7 +1763,8 @@ public void deleteProviderResourceGroupRoles(String tenantDomain, String provide * @param providerDomain name of the provider domain * @param providerServiceName name of the provider service * @param resourceGroup name of the resource group - * @return list of provider roles or ZMSClientException will be thrown in case of failure + * @return list of provider roles + * @throws ZMSClientException in case of failure */ public ProviderResourceGroupRoles getProviderResourceGroupRoles(String tenantDomain, String providerDomain, String providerServiceName, String resourceGroup) { @@ -1714,7 +1782,8 @@ public ProviderResourceGroupRoles getProviderResourceGroupRoles(String tenantDom /** * Check the data for the specified domain object * @param domain name of the domain to be checked - * @return DomainDataCheck object or ZMSClientException will be thrown in case of failure + * @return DomainDataCheck object + * @throws ZMSClientException in case of failure */ public DomainDataCheck getDomainDataCheck(String domain) { updatePrincipal(); @@ -1732,7 +1801,8 @@ public DomainDataCheck getDomainDataCheck(String domain) { * The template object will include the list of roles and policies that will * be provisioned in the domain when the template is applied. * @param template name of the solution template to be retrieved - * @return template object or ZMSClientException will be thrown in case of failure + * @return template object + * @throws ZMSClientException in case of failure */ public Template getTemplate(String template) { updatePrincipal(); @@ -1747,7 +1817,8 @@ public Template getTemplate(String template) { /** * Retrieve the list of solution templates provisioned on the ZMS Server - * @return list of template names or ZMSClientException will be thrown in case of failure + * @return list of template names + * @throws ZMSClientException in case of failure */ public ServerTemplateList getServerTemplateList() { updatePrincipal(); @@ -1765,6 +1836,7 @@ public ServerTemplateList getServerTemplateList() { * @param domain name of the domain to be updated * @param auditRef string containing audit specification or ticket number * @param templates contains list of template names to be provisioned in the domain + * @throws ZMSClientException in case of failure */ public void putDomainTemplate(String domain, String auditRef, DomainTemplate templates) { updatePrincipal(); @@ -1782,6 +1854,7 @@ public void putDomainTemplate(String domain, String auditRef, DomainTemplate tem * @param domain name of the domain to be updated * @param template is the name of the provisioned template to be deleted * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure */ public void deleteDomainTemplate(String domain, String template, String auditRef) { updatePrincipal(); @@ -1798,6 +1871,7 @@ public void deleteDomainTemplate(String domain, String template, String auditRef * Retrieve the list of solution template provisioned for a domain * @param domain name of the domain * @return TemplateList object that includes the list of provisioned solution template names + * @throws ZMSClientException in case of failure */ public DomainTemplateList getDomainTemplateList(String domain) { updatePrincipal(); @@ -1819,6 +1893,7 @@ public DomainTemplateList getDomainTemplateList(String domain) { * request all principals from Athenz Service * @param action optional field specifying what action to filter assertions on * @return ResourceAccessList object that lists the set of assertions per principal + * @throws ZMSClientException in case of failure */ public ResourceAccessList getResourceAccessList(String principal, String action) { updatePrincipal(); @@ -1830,4 +1905,56 @@ public ResourceAccessList getResourceAccessList(String principal, String action) throw new ZMSClientException(ZMSClientException.BAD_REQUEST, ex.getMessage()); } } + + /** + * Retrieve the quota deatails for the specified domain + * @param domainName name of the domain + * @return quota object + * @throws ZMSClientException in case of failure + */ + public Quota getQuota(String domainName) { + updatePrincipal(); + try { + return client.getQuota(domainName); + } catch (ResourceException ex) { + throw new ZMSClientException(ex.getCode(), ex.getData()); + } catch (Exception ex) { + throw new ZMSClientException(ZMSClientException.BAD_REQUEST, ex.getMessage()); + } + } + + /** + * Create/Update the quota details for the specified domain + * @param domainName name of the domain + * @param auditRef string containing audit specification or ticket number + * @param quota object to be set for the domain + * @throws ZMSClientException in case of failure + */ + public void putQuota(String domainName, String auditRef, Quota quota) { + updatePrincipal(); + try { + client.putQuota(domainName, auditRef, quota); + } catch (ResourceException ex) { + throw new ZMSClientException(ex.getCode(), ex.getData()); + } catch (Exception ex) { + throw new ZMSClientException(ZMSClientException.BAD_REQUEST, ex.getMessage()); + } + } + + /** + * Delete the specified quota details for the specified domain + * @param domainName name of the domain + * @param auditRef string containing audit specification or ticket number + * @throws ZMSClientException in case of failure + */ + public void deleteQuota(String domainName, String auditRef) { + updatePrincipal(); + try { + client.deleteQuota(domainName, auditRef); + } catch (ResourceException ex) { + throw new ZMSClientException(ex.getCode(), ex.getData()); + } catch (Exception ex) { + throw new ZMSClientException(ZMSClientException.BAD_REQUEST, ex.getMessage()); + } + } } diff --git a/clients/java/zms/src/main/java/com/yahoo/athenz/zms/ZMSRDLGeneratedClient.java b/clients/java/zms/src/main/java/com/yahoo/athenz/zms/ZMSRDLGeneratedClient.java index c15c4f14637..7cb8978fbfe 100644 --- a/clients/java/zms/src/main/java/com/yahoo/athenz/zms/ZMSRDLGeneratedClient.java +++ b/clients/java/zms/src/main/java/com/yahoo/athenz/zms/ZMSRDLGeneratedClient.java @@ -1537,4 +1537,64 @@ public User deleteUser(String name, String auditRef) { } + public Quota getQuota(String name) { + WebTarget target = base.path("/domain/{name}/quota") + .resolveTemplate("name", name); + Invocation.Builder invocationBuilder = target.request("application/json"); + if (credsHeader != null) { + invocationBuilder = invocationBuilder.header(credsHeader, credsToken); + } + Response response = invocationBuilder.get(); + int code = response.getStatus(); + switch (code) { + case 200: + return response.readEntity(Quota.class); + default: + throw new ResourceException(code, response.readEntity(ResourceError.class)); + } + + } + + public Quota putQuota(String name, String auditRef, Quota quota) { + WebTarget target = base.path("/domain/{name}/quota") + .resolveTemplate("name", name); + Invocation.Builder invocationBuilder = target.request("application/json"); + if (credsHeader != null) { + invocationBuilder = invocationBuilder.header(credsHeader, credsToken); + } + if (auditRef != null) { + invocationBuilder = invocationBuilder.header("Y-Audit-Ref", auditRef); + } + Response response = invocationBuilder.put(javax.ws.rs.client.Entity.entity(quota, "application/json")); + int code = response.getStatus(); + switch (code) { + case 204: + return null; + default: + throw new ResourceException(code, response.readEntity(ResourceError.class)); + } + + } + + public Quota deleteQuota(String name, String auditRef) { + WebTarget target = base.path("/domain/{name}/quota") + .resolveTemplate("name", name); + Invocation.Builder invocationBuilder = target.request("application/json"); + if (credsHeader != null) { + invocationBuilder = invocationBuilder.header(credsHeader, credsToken); + } + if (auditRef != null) { + invocationBuilder = invocationBuilder.header("Y-Audit-Ref", auditRef); + } + Response response = invocationBuilder.delete(); + int code = response.getStatus(); + switch (code) { + case 204: + return null; + default: + throw new ResourceException(code, response.readEntity(ResourceError.class)); + } + + } + } diff --git a/clients/java/zms/src/test/java/com/yahoo/athenz/zms/ZMSClientTest.java b/clients/java/zms/src/test/java/com/yahoo/athenz/zms/ZMSClientTest.java index 90a48e61015..db439c17cd8 100644 --- a/clients/java/zms/src/test/java/com/yahoo/athenz/zms/ZMSClientTest.java +++ b/clients/java/zms/src/test/java/com/yahoo/athenz/zms/ZMSClientTest.java @@ -2157,4 +2157,63 @@ public void testPutUserMeta() { assertEquals(400, ex.getCode()); } } + + @Test + public void testGetQuota() { + ZMSClient client = createClient(systemAdminUser); + ZMSRDLGeneratedClient c = Mockito.mock(ZMSRDLGeneratedClient.class); + client.setZMSRDLGeneratedClient(c); + Quota quota = new Quota().setName("athenz").setAssertion(10).setEntity(11) + .setPolicy(12).setPublicKey(13).setRole(14).setRoleMember(15) + .setService(16).setServiceHost(17).setSubdomain(18); + Mockito.when(c.getQuota("athenz")).thenReturn(quota); + + Quota quotaRes = client.getQuota("athenz"); + assertNotNull(quotaRes); + assertEquals(quotaRes.getPolicy(), 12); + assertEquals(quotaRes.getRole(), 14); + } + + @Test + public void testPutQuota() { + ZMSClient client = createClient(systemAdminUser); + ZMSRDLGeneratedClient c = Mockito.mock(ZMSRDLGeneratedClient.class); + client.setZMSRDLGeneratedClient(c); + Quota quota = new Quota().setName("athenz").setAssertion(10).setEntity(11) + .setPolicy(12).setPublicKey(13).setRole(14).setRoleMember(15) + .setService(16).setServiceHost(17).setSubdomain(18); + Mockito.when(c.putQuota("athenz", AUDIT_REF, quota)).thenReturn(null).thenThrow(new ZMSClientException(400, "fail")); + + // first time it completes successfully + + client.putQuota("athenz", AUDIT_REF, quota); + + // second time it fails + try { + client.putQuota("athenz", AUDIT_REF, quota); + fail(); + } catch (ZMSClientException ex) { + assertEquals(400, ex.getCode()); + } + } + + @Test + public void testDeleteQuota() { + ZMSClient client = createClient(systemAdminUser); + ZMSRDLGeneratedClient c = Mockito.mock(ZMSRDLGeneratedClient.class); + client.setZMSRDLGeneratedClient(c); + Mockito.when(c.deleteQuota("athenz", AUDIT_REF)).thenReturn(null).thenThrow(new ZMSClientException(400, "fail")); + + // first time it completes successfully + + client.deleteQuota("athenz", AUDIT_REF); + + // second time it fails + try { + client.deleteQuota("athenz", AUDIT_REF); + fail(); + } catch (ZMSClientException ex) { + assertEquals(400, ex.getCode()); + } + } } diff --git a/clients/java/zts/src/main/java/com/yahoo/athenz/zts/ResourceException.java b/clients/java/zts/src/main/java/com/yahoo/athenz/zts/ResourceException.java index e972cc06d22..0cb5fa274b7 100644 --- a/clients/java/zts/src/main/java/com/yahoo/athenz/zts/ResourceException.java +++ b/clients/java/zts/src/main/java/com/yahoo/athenz/zts/ResourceException.java @@ -21,10 +21,13 @@ public class ResourceException extends RuntimeException { public final static int GONE = 410; public final static int PRECONDITION_FAILED = 412; public final static int UNSUPPORTED_MEDIA_TYPE = 415; + public final static int PRECONDITION_REQUIRED = 428; + public final static int TOO_MANY_REQUESTS = 429; + public final static int REQUEST_HEADER_FIELDS_TOO_LARGE = 431; public final static int INTERNAL_SERVER_ERROR = 500; public final static int NOT_IMPLEMENTED = 501; - public final static int SERVICE_UNAVAILABLE = 503; + public final static int NETWORK_AUTHENTICATION_REQUIRED = 511; public static String codeToString(int code) { switch (code) { @@ -45,8 +48,13 @@ public static String codeToString(int code) { case GONE: return "Gone"; case PRECONDITION_FAILED: return "Precondition Failed"; case UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type"; + case PRECONDITION_REQUIRED: return "Precondition Required"; + case TOO_MANY_REQUESTS: return "Too Many Requests"; + case REQUEST_HEADER_FIELDS_TOO_LARGE: return "Request Header Fields Too Large"; case INTERNAL_SERVER_ERROR: return "Internal Server Error"; case NOT_IMPLEMENTED: return "Not Implemented"; + case SERVICE_UNAVAILABLE: return "Service Unavailable"; + case NETWORK_AUTHENTICATION_REQUIRED: return "Network Authentication Required"; default: return "" + code; } } diff --git a/core/zms/src/main/java/com/yahoo/athenz/zms/Quota.java b/core/zms/src/main/java/com/yahoo/athenz/zms/Quota.java new file mode 100644 index 00000000000..0d260e8b1e2 --- /dev/null +++ b/core/zms/src/main/java/com/yahoo/athenz/zms/Quota.java @@ -0,0 +1,148 @@ +// +// This file generated by rdl 1.4.13. Do not modify! +// + +package com.yahoo.athenz.zms; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.yahoo.rdl.*; + +// +// Quota - The representation for a quota object +// +public class Quota { + public String name; + public int subdomain; + public int role; + public int roleMember; + public int policy; + public int assertion; + public int entity; + public int service; + public int serviceHost; + public int publicKey; + @RdlOptional + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public Timestamp modified; + + public Quota setName(String name) { + this.name = name; + return this; + } + public String getName() { + return name; + } + public Quota setSubdomain(int subdomain) { + this.subdomain = subdomain; + return this; + } + public int getSubdomain() { + return subdomain; + } + public Quota setRole(int role) { + this.role = role; + return this; + } + public int getRole() { + return role; + } + public Quota setRoleMember(int roleMember) { + this.roleMember = roleMember; + return this; + } + public int getRoleMember() { + return roleMember; + } + public Quota setPolicy(int policy) { + this.policy = policy; + return this; + } + public int getPolicy() { + return policy; + } + public Quota setAssertion(int assertion) { + this.assertion = assertion; + return this; + } + public int getAssertion() { + return assertion; + } + public Quota setEntity(int entity) { + this.entity = entity; + return this; + } + public int getEntity() { + return entity; + } + public Quota setService(int service) { + this.service = service; + return this; + } + public int getService() { + return service; + } + public Quota setServiceHost(int serviceHost) { + this.serviceHost = serviceHost; + return this; + } + public int getServiceHost() { + return serviceHost; + } + public Quota setPublicKey(int publicKey) { + this.publicKey = publicKey; + return this; + } + public int getPublicKey() { + return publicKey; + } + public Quota setModified(Timestamp modified) { + this.modified = modified; + return this; + } + public Timestamp getModified() { + return modified; + } + + @Override + public boolean equals(Object another) { + if (this != another) { + if (another == null || another.getClass() != Quota.class) { + return false; + } + Quota a = (Quota) another; + if (name == null ? a.name != null : !name.equals(a.name)) { + return false; + } + if (subdomain != a.subdomain) { + return false; + } + if (role != a.role) { + return false; + } + if (roleMember != a.roleMember) { + return false; + } + if (policy != a.policy) { + return false; + } + if (assertion != a.assertion) { + return false; + } + if (entity != a.entity) { + return false; + } + if (service != a.service) { + return false; + } + if (serviceHost != a.serviceHost) { + return false; + } + if (publicKey != a.publicKey) { + return false; + } + if (modified == null ? a.modified != null : !modified.equals(a.modified)) { + return false; + } + } + return true; + } +} diff --git a/core/zms/src/main/java/com/yahoo/athenz/zms/ZMSSchema.java b/core/zms/src/main/java/com/yahoo/athenz/zms/ZMSSchema.java index 0c3db19547c..66942849f8b 100644 --- a/core/zms/src/main/java/com/yahoo/athenz/zms/ZMSSchema.java +++ b/core/zms/src/main/java/com/yahoo/athenz/zms/ZMSSchema.java @@ -361,6 +361,20 @@ private static Schema build() { .comment("Set of metadata attributes that system administrators may set on user domains") .field("enabled", "Bool", true, "", true); + sb.structType("Quota") + .comment("The representation for a quota object") + .field("name", "DomainName", false, "name of the domain object") + .field("subdomain", "Int32", false, "number of subdomains allowed (applied at top level domain level)") + .field("role", "Int32", false, "number of roles allowed") + .field("roleMember", "Int32", false, "number of members a role may have") + .field("policy", "Int32", false, "number of policies allowed") + .field("assertion", "Int32", false, "total number of assertions a policy may have") + .field("entity", "Int32", false, "total number of entity objects") + .field("service", "Int32", false, "number of services allowed") + .field("serviceHost", "Int32", false, "number of hosts allowed per service") + .field("publicKey", "Int32", false, "number of public keys per service") + .field("modified", "Timestamp", true, "the last modification timestamp of the quota object"); + sb.resource("Domain", "GET", "/domain/{domain}") .comment("Get info for the specified domain, by name. This request only returns the configured domain attributes and not any domain objects like roles, policies or service identities.") @@ -373,6 +387,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -388,6 +404,8 @@ private static Schema build() { .queryParam("role", "roleName", "ResourceName", null, "restrict the domain names where the specified user is in this role - see roleMember") .headerParam("If-Modified-Since", "modifiedSince", "String", null, "This header specifies to the server to return any domains modified since this HTTP date") .expected("OK") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -401,6 +419,8 @@ private static Schema build() { .exception("FORBIDDEN", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -417,6 +437,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -433,6 +455,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -448,6 +472,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -464,6 +490,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -479,6 +507,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -497,6 +527,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -515,6 +547,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -527,6 +561,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -545,6 +581,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -557,6 +595,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -576,6 +616,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -591,6 +633,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -609,6 +653,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -621,6 +667,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -637,6 +685,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -650,6 +700,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -667,6 +719,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -686,6 +740,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -704,6 +760,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -720,6 +778,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -740,6 +800,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -759,6 +821,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -775,6 +839,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -791,6 +857,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -804,6 +872,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -819,6 +889,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -838,6 +910,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -856,6 +930,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -872,6 +948,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -891,6 +969,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -910,6 +990,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -929,6 +1011,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -944,6 +1028,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -962,6 +1048,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -976,6 +1064,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -992,6 +1082,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1008,6 +1100,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1028,6 +1122,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1047,6 +1143,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1066,6 +1164,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1081,6 +1181,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1099,6 +1201,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1119,6 +1223,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1138,6 +1244,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1158,6 +1266,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1174,6 +1284,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1193,6 +1305,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1214,6 +1328,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1231,6 +1347,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1251,6 +1369,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1272,6 +1392,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1289,6 +1411,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1309,6 +1433,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1326,6 +1452,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1344,6 +1472,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1359,6 +1489,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1374,6 +1506,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1386,6 +1520,8 @@ private static Schema build() { .expected("OK") .exception("FORBIDDEN", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1395,6 +1531,8 @@ private static Schema build() { .queryParam("services", "serviceNames", "String", null, "comma separated list of on-behalf-of service names") .expected("OK") .exception("BAD_REQUEST", "ResourceError", "") + + .exception("TOO_MANY_REQUESTS", "ResourceError", "") ; sb.resource("ServicePrincipal", "GET", "/principal") @@ -1405,6 +1543,8 @@ private static Schema build() { .exception("FORBIDDEN", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1412,6 +1552,8 @@ private static Schema build() { .comment("Get the list of solution templates defined in the server") .auth("", "", true) .expected("OK") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1424,6 +1566,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1431,6 +1575,8 @@ private static Schema build() { .comment("Enumerate users that are registered as principals in the system This will return only the principals with \".\" prefix") .auth("", "", true) .expected("OK") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1449,6 +1595,8 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; @@ -1466,6 +1614,53 @@ private static Schema build() { .exception("NOT_FOUND", "ResourceError", "") + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + + .exception("UNAUTHORIZED", "ResourceError", "") +; + + sb.resource("Quota", "GET", "/domain/{name}/quota") + .comment("Retrieve the quota object defined for the domain") + .pathParam("name", "DomainName", "name of the domain") + .auth("", "", true) + .expected("OK") + .exception("BAD_REQUEST", "ResourceError", "") + + .exception("NOT_FOUND", "ResourceError", "") +; + + sb.resource("Quota", "PUT", "/domain/{name}/quota") + .comment("Update the specified domain's quota object") + .pathParam("name", "DomainName", "name of the domain") + .headerParam("Y-Audit-Ref", "auditRef", "String", null, "Audit reference") + .input("quota", "Quota", "Quota object with limits for the domain") + .auth("update", "sys.auth:quota") + .expected("NO_CONTENT") + .exception("BAD_REQUEST", "ResourceError", "") + + .exception("CONFLICT", "ResourceError", "") + + .exception("FORBIDDEN", "ResourceError", "") + + .exception("NOT_FOUND", "ResourceError", "") + + .exception("UNAUTHORIZED", "ResourceError", "") +; + + sb.resource("Quota", "DELETE", "/domain/{name}/quota") + .comment("Delete the specified domain's quota") + .pathParam("name", "DomainName", "name of the domain") + .headerParam("Y-Audit-Ref", "auditRef", "String", null, "Audit reference") + .auth("update", "sys.auth:quota") + .expected("NO_CONTENT") + .exception("BAD_REQUEST", "ResourceError", "") + + .exception("CONFLICT", "ResourceError", "") + + .exception("FORBIDDEN", "ResourceError", "") + + .exception("NOT_FOUND", "ResourceError", "") + .exception("UNAUTHORIZED", "ResourceError", "") ; diff --git a/core/zms/src/main/rdl/Access.rdli b/core/zms/src/main/rdl/Access.rdli index a363cc8b56d..a478976cba2 100644 --- a/core/zms/src/main/rdl/Access.rdli +++ b/core/zms/src/main/rdl/Access.rdli @@ -29,6 +29,7 @@ resource Access GET "/access/{action}/{resource}?domain={domain}&principal={chec ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError NOT_FOUND; + ResourceError TOO_MANY_REQUESTS; } } @@ -47,6 +48,7 @@ resource Access GET "/access/{action}?resource={resource}&domain={domain}&princi ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError NOT_FOUND; + ResourceError TOO_MANY_REQUESTS; } } @@ -72,5 +74,6 @@ resource ResourceAccessList GET "/resource?principal={principal}&action={action} ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError NOT_FOUND; + ResourceError TOO_MANY_REQUESTS; } } diff --git a/core/zms/src/main/rdl/Domain.rdli b/core/zms/src/main/rdl/Domain.rdli index 3653b48d64b..4108f808441 100644 --- a/core/zms/src/main/rdl/Domain.rdli +++ b/core/zms/src/main/rdl/Domain.rdli @@ -17,6 +17,7 @@ resource Domain GET "/domain/{domain}" { ResourceError NOT_FOUND; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -40,6 +41,7 @@ resource DomainList GET "/domain?limit={limit}&skip={skip}&prefix={prefix}&depth String modifiedSince (header="If-Modified-Since"); //This header specifies to the server to return any domains modified since this HTTP date exceptions { ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -76,6 +78,7 @@ resource Domain POST "/domain" { ResourceError BAD_REQUEST; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -92,6 +95,7 @@ resource Domain POST "/subdomain/{parent}" { ResourceError BAD_REQUEST; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -115,6 +119,7 @@ resource Domain POST "/userdomain/{name}" { ResourceError BAD_REQUEST; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -131,6 +136,7 @@ resource TopLevelDomain DELETE "/domain/{name}" { ResourceError BAD_REQUEST; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -148,6 +154,7 @@ resource SubDomain DELETE "/subdomain/{parent}/{name}" { ResourceError BAD_REQUEST; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -164,6 +171,7 @@ resource UserDomain DELETE "/userdomain/{name}" { ResourceError BAD_REQUEST; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -181,6 +189,7 @@ resource Domain PUT "/domain/{name}/meta" { ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -199,6 +208,7 @@ resource DomainTemplate PUT "/domain/{name}/template" { ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -210,6 +220,7 @@ resource DomainTemplateList GET "/domain/{name}/template" { ResourceError BAD_REQUEST; ResourceError NOT_FOUND; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -229,5 +240,6 @@ resource DomainTemplate DELETE "/domain/{name}/template/{template}" { ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } diff --git a/core/zms/src/main/rdl/DomainDataCheck.rdli b/core/zms/src/main/rdl/DomainDataCheck.rdli index 961475a7765..e119d43915d 100644 --- a/core/zms/src/main/rdl/DomainDataCheck.rdli +++ b/core/zms/src/main/rdl/DomainDataCheck.rdli @@ -47,6 +47,7 @@ resource DomainDataCheck GET "/domain/{domainName}/check" { ResourceError NOT_FOUND; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } diff --git a/core/zms/src/main/rdl/Entity.rdli b/core/zms/src/main/rdl/Entity.rdli index 978b0dba489..721c56557e2 100644 --- a/core/zms/src/main/rdl/Entity.rdli +++ b/core/zms/src/main/rdl/Entity.rdli @@ -19,6 +19,7 @@ resource Entity PUT "/domain/{domainName}/entity/{entityName}" { ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -33,6 +34,7 @@ resource Entity GET "/domain/{domainName}/entity/{entityName}" { ResourceError BAD_REQUEST; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -51,6 +53,7 @@ resource Entity DELETE "/domain/{domainName}/entity/{entityName}" { ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -62,5 +65,6 @@ resource EntityList GET "/domain/{domainName}/entity" { ResourceError BAD_REQUEST; ResourceError NOT_FOUND; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } diff --git a/core/zms/src/main/rdl/Policy.rdli b/core/zms/src/main/rdl/Policy.rdli index 17c20bce7bd..238af603d09 100644 --- a/core/zms/src/main/rdl/Policy.rdli +++ b/core/zms/src/main/rdl/Policy.rdli @@ -22,6 +22,7 @@ resource PolicyList GET "/domain/{domainName}/policy?limit={limit}&skip={skip}" ResourceError NOT_FOUND; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -34,6 +35,7 @@ resource Policies GET "/domain/{domainName}/policies?assertions={assertions}" { ResourceError BAD_REQUEST; ResourceError NOT_FOUND; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -47,6 +49,7 @@ resource Policy GET "/domain/{domainName}/policy/{policyName}" { ResourceError NOT_FOUND; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -64,6 +67,7 @@ resource Policy PUT "/domain/{domainName}/policy/{policyName}" { ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -82,6 +86,7 @@ resource Policy DELETE "/domain/{domainName}/policy/{policyName}" { ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -96,6 +101,7 @@ resource Assertion GET "/domain/{domainName}/policy/{policyName}/assertion/{asse ResourceError NOT_FOUND; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -113,6 +119,7 @@ resource Assertion PUT "/domain/{domainName}/policy/{policyName}/assertion" { ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -132,5 +139,6 @@ resource Assertion DELETE "/domain/{domainName}/policy/{policyName}/assertion/{a ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } \ No newline at end of file diff --git a/core/zms/src/main/rdl/Quota.rdli b/core/zms/src/main/rdl/Quota.rdli new file mode 100644 index 00000000000..7874dbb0c85 --- /dev/null +++ b/core/zms/src/main/rdl/Quota.rdli @@ -0,0 +1,61 @@ +// Copyright 2017 Oath Inc. +// Licensed under the terms of the Apache version 2.0 license. See LICENSE file for terms. + +//User API +include "Names.tdl"; + +//The representation for a quota object +type Quota Struct { + DomainName name; //name of the domain object + Int32 subdomain; //number of subdomains allowed (applied at top level domain level) + Int32 role; //number of roles allowed + Int32 roleMember; //number of members a role may have + Int32 policy; //number of policies allowed + Int32 assertion; //total number of assertions a policy may have + Int32 entity; //total number of entity objects + Int32 service; //number of services allowed + Int32 serviceHost; //number of hosts allowed per service + Int32 publicKey; //number of public keys per service + Timestamp modified (optional); //the last modification timestamp of the quota object +} + +//Retrieve the quota object defined for the domain +resource Quota GET "/domain/{name}/quota" { + DomainName name; //name of the domain + authenticate; + exceptions { + ResourceError NOT_FOUND; + ResourceError BAD_REQUEST; + } +} + +//Update the specified domain's quota object +resource Quota PUT "/domain/{name}/quota" { + DomainName name; //name of the domain + String auditRef (header="Y-Audit-Ref"); //Audit reference + Quota quota; //Quota object with limits for the domain + authorize ("update", "sys.auth:quota"); + expected NO_CONTENT; + exceptions { + ResourceError NOT_FOUND; + ResourceError BAD_REQUEST; + ResourceError FORBIDDEN; + ResourceError UNAUTHORIZED; + ResourceError CONFLICT; + } +} + +//Delete the specified domain's quota +resource Quota DELETE "/domain/{name}/quota" { + DomainName name; //name of the domain + String auditRef (header="Y-Audit-Ref"); //Audit reference + authorize ("update", "sys.auth:quota"); + expected NO_CONTENT; + exceptions { + ResourceError NOT_FOUND; + ResourceError BAD_REQUEST; + ResourceError FORBIDDEN; + ResourceError UNAUTHORIZED; + ResourceError CONFLICT; + } +} diff --git a/core/zms/src/main/rdl/Role.rdli b/core/zms/src/main/rdl/Role.rdli index 0425b622bd5..2129854b78c 100644 --- a/core/zms/src/main/rdl/Role.rdli +++ b/core/zms/src/main/rdl/Role.rdli @@ -15,6 +15,7 @@ resource RoleList GET "/domain/{domainName}/role?limit={limit}&skip={skip}" { ResourceError NOT_FOUND; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -28,6 +29,7 @@ resource Roles GET "/domain/{domainName}/roles?members={members}" { ResourceError BAD_REQUEST; ResourceError NOT_FOUND; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -43,6 +45,7 @@ resource Role GET "/domain/{domainName}/role/{roleName}?auditLog={auditLog}&expa ResourceError NOT_FOUND; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -60,6 +63,7 @@ resource Role PUT "/domain/{domainName}/role/{roleName}" { ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -78,6 +82,7 @@ resource Role DELETE "/domain/{domainName}/role/{roleName}" { ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -92,6 +97,7 @@ resource Membership GET "/domain/{domainName}/role/{roleName}/member/{memberName ResourceError NOT_FOUND; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -110,6 +116,7 @@ resource Membership PUT "/domain/{domainName}/role/{roleName}/member/{memberName ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -129,6 +136,7 @@ resource Membership DELETE "/domain/{domainName}/role/{roleName}/member/{memberN ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -147,5 +155,6 @@ resource DefaultAdmins PUT "/domain/{domainName}/admins" { ResourceError BAD_REQUEST; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } diff --git a/core/zms/src/main/rdl/ServiceIdentity.rdli b/core/zms/src/main/rdl/ServiceIdentity.rdli index e42b77b6168..dd05ec17f33 100644 --- a/core/zms/src/main/rdl/ServiceIdentity.rdli +++ b/core/zms/src/main/rdl/ServiceIdentity.rdli @@ -17,6 +17,7 @@ resource ServiceIdentity PUT "/domain/{domain}/service/{service}" { ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -30,6 +31,7 @@ resource ServiceIdentity GET "/domain/{domain}/service/{service}" { ResourceError BAD_REQUEST; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -48,6 +50,7 @@ resource ServiceIdentity DELETE "/domain/{domain}/service/{service}" { ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -61,6 +64,7 @@ resource ServiceIdentities GET "/domain/{domainName}/services?publickeys={public ResourceError BAD_REQUEST; ResourceError NOT_FOUND; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -75,6 +79,7 @@ resource ServiceIdentityList GET "/domain/{domainName}/service?limit={limit}&ski ResourceError NOT_FOUND; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -89,6 +94,7 @@ resource PublicKeyEntry GET "/domain/{domain}/service/{service}/publickey/{id}" ResourceError NOT_FOUND; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -107,6 +113,7 @@ resource PublicKeyEntry PUT "/domain/{domain}/service/{service}/publickey/{id}" ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -126,5 +133,6 @@ resource PublicKeyEntry DELETE "/domain/{domain}/service/{service}/publickey/{id ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } diff --git a/core/zms/src/main/rdl/SignedDomains.rdli b/core/zms/src/main/rdl/SignedDomains.rdli index e0154b958d5..20cbdd05f00 100644 --- a/core/zms/src/main/rdl/SignedDomains.rdli +++ b/core/zms/src/main/rdl/SignedDomains.rdli @@ -76,5 +76,6 @@ resource SignedDomains GET "/sys/modified_domains?domain={domain}&metaonly={meta ResourceError NOT_FOUND; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } diff --git a/core/zms/src/main/rdl/Template.rdli b/core/zms/src/main/rdl/Template.rdli index 8f85b3f19d1..ef8d1ba81e3 100644 --- a/core/zms/src/main/rdl/Template.rdli +++ b/core/zms/src/main/rdl/Template.rdli @@ -9,6 +9,7 @@ resource ServerTemplateList GET "/template" { authenticate; exceptions { ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -22,5 +23,6 @@ resource Template GET "/template/{template}" { ResourceError BAD_REQUEST; ResourceError NOT_FOUND; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } diff --git a/core/zms/src/main/rdl/Tenancy.rdli b/core/zms/src/main/rdl/Tenancy.rdli index 8934b455fa3..5e9864163cd 100644 --- a/core/zms/src/main/rdl/Tenancy.rdli +++ b/core/zms/src/main/rdl/Tenancy.rdli @@ -31,6 +31,7 @@ resource Tenancy PUT "/domain/{domain}/tenancy/{service}" { ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -44,6 +45,7 @@ resource Tenancy GET "/domain/{domain}/tenancy/{service}" { ResourceError BAD_REQUEST; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -62,6 +64,7 @@ resource Tenancy DELETE "/domain/{domain}/tenancy/{service}" { ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -80,6 +83,7 @@ resource TenancyResourceGroup PUT "/domain/{domain}/tenancy/{service}/resourceGr ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -97,5 +101,6 @@ resource TenancyResourceGroup DELETE "/domain/{domain}/tenancy/{service}/resourc ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } diff --git a/core/zms/src/main/rdl/TenantRoles.rdli b/core/zms/src/main/rdl/TenantRoles.rdli index e23f7ce1f8f..763cde0d19c 100644 --- a/core/zms/src/main/rdl/TenantRoles.rdli +++ b/core/zms/src/main/rdl/TenantRoles.rdli @@ -51,6 +51,7 @@ resource TenantRoles PUT "/domain/{domain}/service/{service}/tenant/{tenantDomai ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -65,6 +66,7 @@ resource TenantRoles GET "/domain/{domain}/service/{service}/tenant/{tenantDomai ResourceError BAD_REQUEST; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -84,6 +86,7 @@ resource TenantRoles DELETE "/domain/{domain}/service/{service}/tenant/{tenantDo ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -103,6 +106,7 @@ resource TenantResourceGroupRoles PUT "/domain/{domain}/service/{service}/tenant ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -118,6 +122,7 @@ resource TenantResourceGroupRoles GET "/domain/{domain}/service/{service}/tenant ResourceError BAD_REQUEST; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -136,6 +141,7 @@ resource TenantResourceGroupRoles DELETE "/domain/{domain}/service/{service}/ten ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -155,6 +161,7 @@ resource ProviderResourceGroupRoles PUT "/domain/{tenantDomain}/provDomain/{prov ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -170,6 +177,7 @@ resource ProviderResourceGroupRoles GET "/domain/{tenantDomain}/provDomain/{prov ResourceError BAD_REQUEST; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -188,5 +196,6 @@ resource ProviderResourceGroupRoles DELETE "/domain/{tenantDomain}/provDomain/{p ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } diff --git a/core/zms/src/main/rdl/Token.rdli b/core/zms/src/main/rdl/Token.rdli index 5e017be5c0c..652844480a7 100644 --- a/core/zms/src/main/rdl/Token.rdli +++ b/core/zms/src/main/rdl/Token.rdli @@ -21,6 +21,7 @@ resource UserToken GET "/user/{userName}/token?services={serviceNames}&header={h exceptions { ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -31,6 +32,7 @@ resource UserToken OPTIONS "/user/{userName}/token?services={serviceNames}" { String serviceNames (optional); //comma separated list of on-behalf-of service names exceptions { ResourceError BAD_REQUEST; + ResourceError TOO_MANY_REQUESTS; } } @@ -50,5 +52,6 @@ resource ServicePrincipal GET "/principal" { ResourceError BAD_REQUEST; ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } diff --git a/core/zms/src/main/rdl/User.rdli b/core/zms/src/main/rdl/User.rdli index f11ad8dd5e1..0728d4b3038 100644 --- a/core/zms/src/main/rdl/User.rdli +++ b/core/zms/src/main/rdl/User.rdli @@ -25,6 +25,7 @@ resource UserList GET "/user" { authenticate; exceptions { ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; } } @@ -45,6 +46,7 @@ resource UserMeta PUT "/user/{name}/meta" { ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } @@ -64,5 +66,6 @@ resource User DELETE "/user/{name}" { ResourceError FORBIDDEN; ResourceError UNAUTHORIZED; ResourceError CONFLICT; + ResourceError TOO_MANY_REQUESTS; } } diff --git a/core/zms/src/main/rdl/ZMS.rdl b/core/zms/src/main/rdl/ZMS.rdl index 644206e5563..f3d54523010 100644 --- a/core/zms/src/main/rdl/ZMS.rdl +++ b/core/zms/src/main/rdl/ZMS.rdl @@ -22,3 +22,4 @@ include "SignedDomains.rdli"; include "Token.rdli"; include "Template.rdli"; include "User.rdli"; +include "Quota.rdli"; diff --git a/core/zms/src/test/java/com/yahoo/athenz/zms/ZMSCoreTest.java b/core/zms/src/test/java/com/yahoo/athenz/zms/ZMSCoreTest.java index 04356b1d236..35199fc801d 100644 --- a/core/zms/src/test/java/com/yahoo/athenz/zms/ZMSCoreTest.java +++ b/core/zms/src/test/java/com/yahoo/athenz/zms/ZMSCoreTest.java @@ -1540,4 +1540,49 @@ public void testUserMetaMethod() { assertFalse(um3.getEnabled()); assertFalse(um1.equals(um3)); } + + @Test + public void testQuotaObject() { + Schema schema = ZMSSchema.instance(); + Validator validator = new Validator(schema); + + Quota quota = new Quota().setName("athenz").setAssertion(10).setEntity(11) + .setPolicy(12).setPublicKey(13).setRole(14).setRoleMember(15) + .setService(16).setServiceHost(17).setSubdomain(18); + + Result result = validator.validate(quota, "Quota"); + assertTrue(result.valid); + + assertEquals(quota.getName(), "athenz"); + assertEquals(quota.getAssertion(), 10); + assertEquals(quota.getEntity(), 11); + assertEquals(quota.getPolicy(), 12); + assertEquals(quota.getPublicKey(), 13); + assertEquals(quota.getRole(), 14); + assertEquals(quota.getRoleMember(), 15); + assertEquals(quota.getService(), 16); + assertEquals(quota.getServiceHost(), 17); + assertEquals(quota.getSubdomain(), 18); + + Quota quota2 = new Quota().setName("athenz").setAssertion(10).setEntity(11) + .setPolicy(12).setPublicKey(13).setRole(14).setRoleMember(15) + .setService(16).setServiceHost(17).setSubdomain(18); + + assertTrue(quota.equals(quota2)); + + quota2.setPolicy(101); + assertFalse(quota.equals(quota2)); + quota2.setPolicy(12); + assertTrue(quota.equals(quota2)); + + quota2.setRole(102); + assertFalse(quota.equals(quota2)); + quota2.setRole(14); + assertTrue(quota.equals(quota2)); + + quota2.setService(103); + assertFalse(quota.equals(quota2)); + quota2.setService(16); + assertTrue(quota.equals(quota2)); + } } diff --git a/libs/go/zmscli/cli.go b/libs/go/zmscli/cli.go index 0fa2549950b..05a10b87b89 100644 --- a/libs/go/zmscli/cli.go +++ b/libs/go/zmscli/cli.go @@ -531,6 +531,18 @@ func (cli *Zms) EvalCommand(params []string) (*string, error) { if argc == 1 { return cli.DeleteDomainTemplate(dn, args[0]) } + case "get-quota": + if argc == 0 { + return cli.GetQuota(dn) + } + case "set-quota": + if argc >= 1 { + return cli.SetQuota(dn, args[0:]) + } + case "delete-quota": + if argc == 0 { + return cli.DeleteQuota(dn) + } default: return nil, fmt.Errorf("Unrecognized command '%v'. Type 'zms-cli help' to see help information", cmd) } @@ -1478,6 +1490,43 @@ func (cli Zms) HelpSpecificCommand(interactive bool, cmd string) string { buf.WriteString(" user : id of the user to be disabled\n") buf.WriteString(" examples:\n") buf.WriteString(" disable-user jdoe\n") + case "get-quota": + buf.WriteString(" syntax:\n") + buf.WriteString(" " + domain_param + " get-quota\n") + buf.WriteString(" parameters:\n") + if !interactive { + buf.WriteString(" domain : name of the domain\n") + } + buf.WriteString(" examples:\n") + buf.WriteString(" " + domain_example + " get-quota\n") + case "delete-quota": + buf.WriteString(" syntax:\n") + buf.WriteString(" " + domain_param + " delete-quota\n") + buf.WriteString(" parameters:\n") + if !interactive { + buf.WriteString(" domain : name of the domain\n") + } + buf.WriteString(" examples:\n") + buf.WriteString(" " + domain_example + " delete-quota\n") + case "set-quota": + buf.WriteString(" syntax:\n") + buf.WriteString(" " + domain_param + " set-quota [quota-attributes ...]\n") + buf.WriteString(" parameters:\n") + if !interactive { + buf.WriteString(" domain : name of the domain\n") + } + buf.WriteString(" quota-attributes : object= values specifying limits. Valid object values are:\n") + buf.WriteString(" : subdomain (applies to top level domains ony)\n") + buf.WriteString(" : role\n") + buf.WriteString(" : role-member\n") + buf.WriteString(" : policy\n") + buf.WriteString(" : assertion (total number across all policies)\n") + buf.WriteString(" : entity\n") + buf.WriteString(" : service\n") + buf.WriteString(" : service-host\n") + buf.WriteString(" : public-key\n") + buf.WriteString(" examples:\n") + buf.WriteString(" " + domain_example + " set-quota role=100 policy=50 service=25\n") default: if interactive { buf.WriteString("Unknown command. Type 'help' to see available commands") @@ -1508,6 +1557,9 @@ func (cli Zms) HelpListCommand() string { buf.WriteString(" get-signed-domains [matching_tag]\n") buf.WriteString(" use-domain [domain]\n") buf.WriteString(" check-domain [domain]\n") + buf.WriteString(" get-quota\n") + buf.WriteString(" set-quota [attrs ...]\n") + buf.WriteString(" delete-quota\n") buf.WriteString("\n") buf.WriteString(" Policy commands:\n") buf.WriteString(" list-policy\n") diff --git a/libs/go/zmscli/dump.go b/libs/go/zmscli/dump.go index f9d9f77ed57..c348570ebaa 100644 --- a/libs/go/zmscli/dump.go +++ b/libs/go/zmscli/dump.go @@ -457,3 +457,47 @@ func (cli Zms) dumpProfile(buf *bytes.Buffer, name, content string) { buf.WriteString(name) cli.dumpMultilineString(buf, content, indent_level1) } + +func (cli Zms) dumpQuota(buf *bytes.Buffer, quota *zms.Quota) { + buf.WriteString("quota:\n") + buf.WriteString(indent_level1) + buf.WriteString("subdomain: ") + buf.WriteString(strconv.Itoa(int(quota.Subdomain))) + buf.WriteString("\n") + buf.WriteString(indent_level1) + buf.WriteString("role: ") + buf.WriteString(strconv.Itoa(int(quota.Role))) + buf.WriteString("\n") + buf.WriteString(indent_level1) + buf.WriteString("role-member: ") + buf.WriteString(strconv.Itoa(int(quota.RoleMember))) + buf.WriteString("\n") + buf.WriteString(indent_level1) + buf.WriteString("policy: ") + buf.WriteString(strconv.Itoa(int(quota.Policy))) + buf.WriteString("\n") + buf.WriteString(indent_level1) + buf.WriteString("assertion: ") + buf.WriteString(strconv.Itoa(int(quota.Assertion))) + buf.WriteString("\n") + buf.WriteString(indent_level1) + buf.WriteString("service: ") + buf.WriteString(strconv.Itoa(int(quota.Service))) + buf.WriteString("\n") + buf.WriteString(indent_level1) + buf.WriteString("service-host: ") + buf.WriteString(strconv.Itoa(int(quota.ServiceHost))) + buf.WriteString("\n") + buf.WriteString(indent_level1) + buf.WriteString("public-key: ") + buf.WriteString(strconv.Itoa(int(quota.PublicKey))) + buf.WriteString("\n") + buf.WriteString(indent_level1) + buf.WriteString("entity: ") + buf.WriteString(strconv.Itoa(int(quota.Entity))) + buf.WriteString("\n") + buf.WriteString(indent_level1) + buf.WriteString("modified: ") + buf.WriteString(quota.Modified.String()) + buf.WriteString("\n") +} diff --git a/libs/go/zmscli/quota.go b/libs/go/zmscli/quota.go new file mode 100644 index 00000000000..7f409626f8d --- /dev/null +++ b/libs/go/zmscli/quota.go @@ -0,0 +1,80 @@ +// Copyright 2017 Oath Inc. +// Licensed under the terms of the Apache version 2.0 license. See LICENSE file for terms. + +package zmscli + +import ( + "bytes" + "strconv" + "strings" + + "github.com/yahoo/athenz/clients/go/zms" +) + +func (cli Zms) DeleteQuota(dn string) (*string, error) { + err := cli.Zms.DeleteQuota(zms.DomainName(dn), cli.AuditRef) + if err == nil { + s := "[Removed quota for domain " + dn + "]" + return &s, nil + } + return nil, err +} + +func (cli Zms) GetQuota(dn string) (*string, error) { + + quota, err := cli.Zms.GetQuota(zms.DomainName(dn)) + if err != nil { + return nil, err + } + + var buf bytes.Buffer + cli.dumpQuota(&buf, quota) + s := buf.String() + return &s, nil +} + +func (cli Zms) SetQuota(dn string, attrs []string) (*string, error) { + quota, err := cli.Zms.GetQuota(zms.DomainName(dn)) + if err != nil { + return nil, err + } + + for _, attr := range attrs { + idx := strings.Index(attr, "=") + if idx == -1 { + continue + } + key := attr[0:idx] + value, err := strconv.Atoi(attr[idx+1:]) + if err != nil { + continue + } + switch key { + case "role": + quota.Role = int32(value) + case "role-member": + quota.RoleMember = int32(value) + case "subdomain": + quota.Subdomain = int32(value) + case "policy": + quota.Policy = int32(value) + case "assertion": + quota.Assertion = int32(value) + case "service": + quota.Service = int32(value) + case "service-host": + quota.ServiceHost = int32(value) + case "public-key": + quota.PublicKey = int32(value) + case "entity": + quota.Entity = int32(value) + } + } + quota.Name = zms.DomainName(dn) + err = cli.Zms.PutQuota(zms.DomainName(dn), cli.AuditRef, quota) + if err != nil { + return nil, err + } + s := "[domain " + dn + " quota successfully set]\n" + return &s, nil +} diff --git a/servers/zms/conf/zms.properties b/servers/zms/conf/zms.properties index 3eea0d325d4..ccff6bcc1dd 100644 --- a/servers/zms/conf/zms.properties +++ b/servers/zms/conf/zms.properties @@ -201,4 +201,36 @@ athenz.zms.solution_templates_fname=${ROOT}/conf/zms_server/solution_templates.j # If the ZMS servlet is deployed along other servlets that may # run on non-TLS ports, this setting forces that requests to # ZMS are only accepted on secure TLS ports. -#athenz.zms.secure_requests_only=true \ No newline at end of file +#athenz.zms.secure_requests_only=true + +# Quota Support: default number of roles allowed to be created +# in a given domain. +#athenz.zms.quota_role=1000 + +# Quota Support: default number of members a single role may have +#athenz.zms.quota_role_member=100 + +# Quota Support: default number of polices allowed to be created +# in a given domain. +#athenz.zms.quota_policy=1000 + +# Quota Support: default number of assertions each policy may have +#athenz.zms.quota_assertion=100 + +# Quota Support: default number of services allowed to be created +# in a given domain. +#athenz.zms.quota_service=250 + +# Quota Support: default number of hosts each service may have +#athenz.zms.quota_service_host=10 + +# Quota Support: default number of public keys each service may have +#athenz.zms.quota_public_key=100 + +# Quota Support: default number of entities allowed to be created +# in a given domain +#athenz.zms.quota_entity=100 + +# Quota Support: default number of sub-domains each top level +# domain allowed to have. +#athenz.zms.quota_subdomain=100 diff --git a/servers/zms/schema/zms_server.mwb b/servers/zms/schema/zms_server.mwb index a878b0693c16a3cced2cb22cbc790d2eb28f4b90..379885cada452180cf8719eec9e36ccf801a01bf 100644 GIT binary patch delta 23943 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 delta 22374 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 diff --git a/servers/zms/schema/zms_server.sql b/servers/zms/schema/zms_server.sql index 18fc7a69d57..2e03d166b4d 100644 --- a/servers/zms/schema/zms_server.sql +++ b/servers/zms/schema/zms_server.sql @@ -1,5 +1,5 @@ -- MySQL Script generated by MySQL Workbench --- Fri May 19 22:44:34 2017 +-- Thu Jun 29 21:28:05 2017 -- Model: New Model Version: 1.0 -- MySQL Workbench Forward Engineering @@ -264,6 +264,31 @@ CREATE TABLE IF NOT EXISTS `zms_server`.`role_audit_log` ( ENGINE = InnoDB; +-- ----------------------------------------------------- +-- Table `zms_server`.`quota` +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS `zms_server`.`quota` ( + `domain_id` INT UNSIGNED NOT NULL, + `subdomain` INT UNSIGNED NOT NULL, + `role` INT UNSIGNED NOT NULL, + `role_member` INT UNSIGNED NOT NULL, + `policy` INT UNSIGNED NOT NULL, + `assertion` INT UNSIGNED NOT NULL, + `entity` INT UNSIGNED NOT NULL, + `service` INT UNSIGNED NOT NULL, + `service_host` INT UNSIGNED NOT NULL, + `public_key` INT UNSIGNED NOT NULL, + `modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`domain_id`), + CONSTRAINT `fk_quota_domain1` + FOREIGN KEY (`domain_id`) + REFERENCES `zms_server`.`domain` (`domain_id`) + ON DELETE CASCADE + ON UPDATE NO ACTION) +ENGINE = InnoDB; + + SET SQL_MODE=@OLD_SQL_MODE; SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/servers/zms/src/main/java/com/yahoo/athenz/provider/ResourceException.java b/servers/zms/src/main/java/com/yahoo/athenz/provider/ResourceException.java index 3a932ad60c3..1ef3fb5de7a 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/provider/ResourceException.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/provider/ResourceException.java @@ -21,10 +21,13 @@ public class ResourceException extends RuntimeException { public final static int GONE = 410; public final static int PRECONDITION_FAILED = 412; public final static int UNSUPPORTED_MEDIA_TYPE = 415; + public final static int PRECONDITION_REQUIRED = 428; + public final static int TOO_MANY_REQUESTS = 429; + public final static int REQUEST_HEADER_FIELDS_TOO_LARGE = 431; public final static int INTERNAL_SERVER_ERROR = 500; public final static int NOT_IMPLEMENTED = 501; - public final static int SERVICE_UNAVAILABLE = 503; + public final static int NETWORK_AUTHENTICATION_REQUIRED = 511; public static String codeToString(int code) { switch (code) { @@ -45,8 +48,13 @@ public static String codeToString(int code) { case GONE: return "Gone"; case PRECONDITION_FAILED: return "Precondition Failed"; case UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type"; + case PRECONDITION_REQUIRED: return "Precondition Required"; + case TOO_MANY_REQUESTS: return "Too Many Requests"; + case REQUEST_HEADER_FIELDS_TOO_LARGE: return "Request Header Fields Too Large"; case INTERNAL_SERVER_ERROR: return "Internal Server Error"; case NOT_IMPLEMENTED: return "Not Implemented"; + case SERVICE_UNAVAILABLE: return "Service Unavailable"; + case NETWORK_AUTHENTICATION_REQUIRED: return "Network Authentication Required"; default: return "" + code; } } 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 fdda2422c9f..440a3ec017f 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 @@ -48,6 +48,7 @@ public class DBService { String userDomain; AuditLogger auditLogger; Cache cacheStore; + QuotaChecker quotaCheck; int retrySleepTime = 250; int defaultRetryCount = 120; int defaultOpTimeout = 60; @@ -88,6 +89,10 @@ public DBService(ObjectStore store, AuditLogger auditLogger, String userDomain) if (retrySleepTime < 0) { retrySleepTime = 250; } + + // create our quota checker class + + quotaCheck = new QuotaChecker(); } static class DataCache { @@ -210,6 +215,11 @@ Domain makeDomain(ResourceContext ctx, String domainName, String description, St do { try (ObjectStoreConnection con = store.getConnection(false)) { + // before adding this domain we need to verify our + // quota check for sub-domains + + quotaCheck.checkSubdomainQuota(con, domainName, caller); + boolean objectsInserted = con.insertDomain(domain); if (!objectsInserted) { con.rollbackChanges(); @@ -701,6 +711,10 @@ void executePutPolicy(ResourceContext ctx, String domainName, String policyName, checkDomainAuditEnabled(con, domainName, auditRef, caller); + // check that quota is not exceeded + + quotaCheck.checkPolicyQuota(con, domainName, policy, caller); + // retrieve our original policy Policy originalPolicy = getPolicy(con, domainName, policyName); @@ -744,6 +758,10 @@ void executePutRole(ResourceContext ctx, String domainName, String roleName, Rol checkDomainAuditEnabled(con, domainName, auditRef, caller); + // check that quota is not exceeded + + quotaCheck.checkRoleQuota(con, domainName, role, caller); + // retrieve our original role Role originalRole = getRole(con, domainName, roleName, false, false); @@ -776,7 +794,7 @@ void executePutRole(ResourceContext ctx, String domainName, String roleName, Rol retryCount -= 1; } while (retryCount > 0); } - + void executePutServiceIdentity(ResourceContext ctx, String domainName, String serviceName, ServiceIdentity service, String auditRef, String caller) { @@ -788,6 +806,10 @@ void executePutServiceIdentity(ResourceContext ctx, String domainName, String se checkDomainAuditEnabled(con, domainName, auditRef, caller); + // check that quota is not exceeded + + quotaCheck.checkServiceIdentityQuota(con, domainName, service, caller); + // retrieve our original service identity object ServiceIdentity originalService = getServiceIdentity(con, domainName, serviceName); @@ -833,7 +855,16 @@ void executePutPublicKeyEntry(ResourceContext ctx, String domainName, String ser // check to see if this key already exists or not - PublicKeyEntry originalKeyEntry = con.getPublicKeyEntry(domainName, serviceName, keyEntry.getId(), false); + PublicKeyEntry originalKeyEntry = con.getPublicKeyEntry(domainName, serviceName, + keyEntry.getId(), false); + + // now we need verify our quota check if we know that + // that we'll be adding another public key + + if (originalKeyEntry == null) { + final List publicKeys = con.listPublicKeys(domainName, serviceName); + quotaCheck.checkServiceIdentityPublicKeyQuota(con, domainName, publicKeys, caller); + } // now process the request @@ -951,7 +982,7 @@ void executePutMembership(ResourceContext ctx, String domainName, String roleNam // first verify that auditing requirements are met checkDomainAuditEnabled(con, domainName, auditRef, caller); - + // before inserting a member we need to verify that // this is a group role and not a delegated one. @@ -961,6 +992,11 @@ void executePutMembership(ResourceContext ctx, String domainName, String roleNam "is a delegated role", caller); } + // now we need verify our quota check + + quotaCheck.checkRoleMembershipQuota(con, domainName, + con.listRoleMembers(domainName, roleName), caller); + // process our insert role member support. since this is a "single" // operation, we are not using any transactions. @@ -1010,6 +1046,10 @@ void executePutEntity(ResourceContext ctx, String domainName, String entityName, checkDomainAuditEnabled(con, domainName, auditRef, caller); + // check that quota is not exceeded + + quotaCheck.checkEntityQuota(con, domainName, entity, caller); + // check to see if this key already exists or not Entity originalEntity = con.getEntity(domainName, entityName); @@ -1800,6 +1840,11 @@ public void executePutAssertion(ResourceContext ctx, String domainName, String p checkDomainAuditEnabled(con, domainName, auditRef, caller); + // now we need verify our quota check + + quotaCheck.checkPolicyAssertionQuota(con, domainName, + con.listAssertions(domainName, policyName), caller); + // process our insert assertion. since this is a "single" // operation, we are not using any transactions. @@ -3000,4 +3045,73 @@ void auditLogUserMeta(StringBuilder auditDetails, UserMeta meta) { auditDetails.append("{\"enabled\": \"").append(meta.getEnabled()) .append("\"}"); } + + public void executePutQuota(ResourceContext ctx, String domainName, Quota quota, + String auditRef, String caller) { + + int retryCount = defaultRetryCount; + do { + try (ObjectStoreConnection con = store.getConnection(true)) { + + // process our insert quota. since this is a "single" + // operation, we are not using any transactions. + + if (con.getQuota(domainName) != null) { + con.updateQuota(domainName, quota); + } else { + con.insertQuota(domainName, quota); + } + + auditLogRequest(ctx, domainName, auditRef, caller, ZMSConsts.HTTP_PUT, + domainName, null); + + return; + + } catch (ResourceException ex) { + + // otherwise check if we need to retry or return failure + + if (!shouldRetryOperation(ex, retryCount)) { + throw ex; + } + } + retryCount -= 1; + } while (retryCount > 0); + } + + public void executeDeleteQuota(ResourceContext ctx, String domainName, + String auditRef, String caller) { + + int retryCount = defaultRetryCount; + do { + try (ObjectStoreConnection con = store.getConnection(true)) { + + // process our delete quota request - it's a single + // operation so no need to make it a transaction + + if (!con.deleteQuota(domainName)) { + throw ZMSUtils.notFoundError(caller + ": unable to delete quota: " + domainName, caller); + } + + // audit log the request + + auditLogRequest(ctx, domainName, auditRef, caller, ZMSConsts.HTTP_DELETE, + domainName, null); + + return; + + } catch (ResourceException ex) { + if (!shouldRetryOperation(ex, retryCount)) { + throw ex; + } + } + retryCount -= 1; + } while (retryCount > 0); + } + + public Quota getQuota(String domainName) { + try (ObjectStoreConnection con = store.getConnection(true)) { + return quotaCheck.getDomainQuota(con, domainName); + } + } } diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/QuotaChecker.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/QuotaChecker.java new file mode 100644 index 00000000000..d85c2ef6eb4 --- /dev/null +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/QuotaChecker.java @@ -0,0 +1,243 @@ +package com.yahoo.athenz.zms; + +import java.util.List; + +import com.yahoo.athenz.zms.store.ObjectStoreConnection; +import com.yahoo.athenz.zms.utils.ZMSUtils; +import com.yahoo.rdl.Timestamp; + +public class QuotaChecker { + + Quota defaultQuota; + + public QuotaChecker() { + int roleQuota = Integer.parseInt(System.getProperty(ZMSConsts.ZMS_PROP_QUOTA_ROLE, "1000")); + int roleMemberQuota = Integer.parseInt(System.getProperty(ZMSConsts.ZMS_PROP_QUOTA_ROLE_MEMBER, "100")); + int policyQuota = Integer.parseInt(System.getProperty(ZMSConsts.ZMS_PROP_QUOTA_POLICY, "1000")); + int assertionQuota = Integer.parseInt(System.getProperty(ZMSConsts.ZMS_PROP_QUOTA_ASSERTION, "100")); + int serviceQuota = Integer.parseInt(System.getProperty(ZMSConsts.ZMS_PROP_QUOTA_SERVICE, "250")); + int serviceHostQuota = Integer.parseInt(System.getProperty(ZMSConsts.ZMS_PROP_QUOTA_SERVICE_HOST, "10")); + int publicKeyQuota = Integer.parseInt(System.getProperty(ZMSConsts.ZMS_PROP_QUOTA_PUBLIC_KEY, "100")); + int entityQuota = Integer.parseInt(System.getProperty(ZMSConsts.ZMS_PROP_QUOTA_ENTITY, "100")); + int subDomainQuota = Integer.parseInt(System.getProperty(ZMSConsts.ZMS_PROP_QUOTA_SUBDOMAIN, "100")); + + defaultQuota = new Quota().setName("server-default") + .setAssertion(assertionQuota).setEntity(entityQuota) + .setPolicy(policyQuota).setPublicKey(publicKeyQuota) + .setRole(roleQuota).setRoleMember(roleMemberQuota) + .setService(serviceQuota).setServiceHost(serviceHostQuota) + .setSubdomain(subDomainQuota).setModified(Timestamp.fromCurrentTime()); + } + + public Quota getDomainQuota(ObjectStoreConnection con, String domainName) { + Quota quota = con.getQuota(domainName); + return (quota == null) ? defaultQuota : quota; + } + + int getListSize(List list) { + return (list == null) ? 0 : list.size(); + } + + void checkSubdomainQuota(ObjectStoreConnection con, String domainName, String caller) { + + // for sub-domains we need to run the quota check against + // the top level domain so let's get that first. If we are + // creating a top level domain then there is no need for + // quota check + + int idx = domainName.indexOf("."); + if (idx == -1) { + return; + } + + final String topLevelDomain = domainName.substring(0, idx); + + // now get the quota for the top level domain + + final Quota quota = getDomainQuota(con, topLevelDomain); + + // get the list of sub-domains for our given top level domain + + final String domainPrefix = topLevelDomain + "."; + int objectCount = con.listDomains(domainPrefix, 0).size() + 1; + + if (quota.getSubdomain() < objectCount) { + throw ZMSUtils.quotaLimitError("subdomain quota exceeded - limit: " + + quota.getSubdomain() + " actual: " + objectCount, caller); + } + } + + void checkRoleQuota(ObjectStoreConnection con, String domainName, Role role, String caller) { + + // if our role is null then there is no quota check + + if (role == null) { + return; + } + + // first retrieve the domain quota + + final Quota quota = getDomainQuota(con, domainName); + + // first we're going to verify the elements that do not + // require any further data from the object store + + int objectCount = getListSize(role.getRoleMembers()); + if (quota.getRoleMember() < objectCount) { + throw ZMSUtils.quotaLimitError("role member quota exceeded - limit: " + + quota.getRoleMember() + " actual: " + objectCount, caller); + } + + // now we're going to check if we'll be allowed + // to create this role in the domain + + objectCount = con.listRoles(domainName).size() + 1; + if (quota.getRole() < objectCount) { + throw ZMSUtils.quotaLimitError("role quota exceeded - limit: " + + quota.getRole() + " actual: " + objectCount, caller); + } + } + + void checkRoleMembershipQuota(ObjectStoreConnection con, String domainName, + List roleMembers, String caller) { + + // first retrieve the domain quota + + final Quota quota = getDomainQuota(con, domainName); + + // now check to make sure we can add 1 more member + // to this role without exceeding the quota + + int objectCount = getListSize(roleMembers) + 1; + if (quota.getRoleMember() < objectCount) { + throw ZMSUtils.quotaLimitError("role member quota exceeded - limit: " + + quota.getRoleMember() + " actual: " + objectCount, caller); + } + } + + void checkPolicyQuota(ObjectStoreConnection con, String domainName, Policy policy, String caller) { + + // if our policy is null then there is no quota check + + if (policy == null) { + return; + } + + // first retrieve the domain quota + + final Quota quota = getDomainQuota(con, domainName); + + // first we're going to verify the elements that do not + // require any further data from the object store + + int objectCount = getListSize(policy.getAssertions()); + if (quota.getAssertion() < objectCount) { + throw ZMSUtils.quotaLimitError("policy assertion quota exceeded - limit: " + + quota.getAssertion() + " actual: " + objectCount, caller); + } + + // now we're going to check if we'll be allowed + // to create this policy in the domain + + objectCount = con.listPolicies(domainName, null).size() + 1; + if (quota.getPolicy() < objectCount) { + throw ZMSUtils.quotaLimitError("policy quota exceeded - limit: " + + quota.getPolicy() + " actual: " + objectCount, caller); + } + } + + void checkPolicyAssertionQuota(ObjectStoreConnection con, String domainName, + List assertions, String caller) { + + // first retrieve the domain quota + + final Quota quota = getDomainQuota(con, domainName); + + // now check to make sure we can add 1 more assertion + // to this policy without exceeding the quota + + int objectCount = getListSize(assertions) + 1; + if (quota.getAssertion() < objectCount) { + throw ZMSUtils.quotaLimitError("policy assertion quota exceeded - limit: " + + quota.getAssertion() + " actual: " + objectCount, caller); + } + } + + void checkServiceIdentityQuota(ObjectStoreConnection con, String domainName, + ServiceIdentity service, String caller) { + + // if our service is null then there is no quota check + + if (service == null) { + return; + } + + // first retrieve the domain quota + + final Quota quota = getDomainQuota(con, domainName); + + // first we're going to verify the elements that do not + // require any further data from the object store + + int objectCount = getListSize(service.getHosts()); + if (quota.getServiceHost() < objectCount) { + throw ZMSUtils.quotaLimitError("service host quota exceeded - limit: " + + quota.getServiceHost() + " actual: " + objectCount, caller); + } + + objectCount = getListSize(service.getPublicKeys()); + if (quota.getPublicKey() < objectCount) { + throw ZMSUtils.quotaLimitError("service public key quota exceeded - limit: " + + quota.getPublicKey() + " actual: " + objectCount, caller); + } + + // now we're going to check if we'll be allowed + // to create this service in the domain + + objectCount = con.listServiceIdentities(domainName).size() + 1; + if (quota.getService() < objectCount) { + throw ZMSUtils.quotaLimitError("service quota exceeded - limit: " + + quota.getService() + " actual: " + objectCount, caller); + } + } + + void checkServiceIdentityPublicKeyQuota(ObjectStoreConnection con, String domainName, + List publicKeys, String caller) { + + // first retrieve the domain quota + + final Quota quota = getDomainQuota(con, domainName); + + // now check to make sure we can add 1 more public key + // to this policy without exceeding the quota + + int objectCount = getListSize(publicKeys) + 1; + if (quota.getPublicKey() < objectCount) { + throw ZMSUtils.quotaLimitError("service public key quota exceeded - limit: " + + quota.getPublicKey() + " actual: " + objectCount, caller); + } + } + + void checkEntityQuota(ObjectStoreConnection con, String domainName, Entity entity, + String caller) { + + // if our entity is null then there is no quota check + + if (entity == null) { + return; + } + + // first retrieve the domain quota + + final Quota quota = getDomainQuota(con, domainName); + + // we're going to check if we'll be allowed + // to create this entity in the domain + + int objectCount = con.listEntities(domainName).size() + 1; + if (quota.getEntity() < objectCount) { + throw ZMSUtils.quotaLimitError("entity quota exceeded - limit: " + + quota.getEntity() + " actual: " + objectCount, caller); + } + } +} diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/ResourceException.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/ResourceException.java index 7800dfaea6b..76478d6e64c 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/ResourceException.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/ResourceException.java @@ -21,10 +21,13 @@ public class ResourceException extends RuntimeException { public final static int GONE = 410; public final static int PRECONDITION_FAILED = 412; public final static int UNSUPPORTED_MEDIA_TYPE = 415; + public final static int PRECONDITION_REQUIRED = 428; + public final static int TOO_MANY_REQUESTS = 429; + public final static int REQUEST_HEADER_FIELDS_TOO_LARGE = 431; public final static int INTERNAL_SERVER_ERROR = 500; public final static int NOT_IMPLEMENTED = 501; - public final static int SERVICE_UNAVAILABLE = 503; + public final static int NETWORK_AUTHENTICATION_REQUIRED = 511; public static String codeToString(int code) { switch (code) { @@ -45,8 +48,13 @@ public static String codeToString(int code) { case GONE: return "Gone"; case PRECONDITION_FAILED: return "Precondition Failed"; case UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type"; + case PRECONDITION_REQUIRED: return "Precondition Required"; + case TOO_MANY_REQUESTS: return "Too Many Requests"; + case REQUEST_HEADER_FIELDS_TOO_LARGE: return "Request Header Fields Too Large"; case INTERNAL_SERVER_ERROR: return "Internal Server Error"; case NOT_IMPLEMENTED: return "Not Implemented"; + case SERVICE_UNAVAILABLE: return "Service Unavailable"; + case NETWORK_AUTHENTICATION_REQUIRED: return "Network Authentication Required"; default: return "" + code; } } 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 f1b07b10a24..0109b25d8fa 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 @@ -40,6 +40,7 @@ public final class ZMSConsts { public static final String ZMS_PROP_JDBC_USER = "athenz.zms.jdbc_user"; public static final String ZMS_PROP_JDBC_PASSWORD = "athenz.zms.jdbc_password"; public static final String ZMS_PROP_FILE_STORE_NAME = "athenz.zms.file_store_name"; + public static final String ZMS_PROP_FILE_STORE_QUOTA = "athenz.zms.file_store_quota"; public static final String ZMS_PROP_FILE_STORE_PATH = "athenz.zms.file_store_path"; public static final String ZMS_PROP_MAX_THREADS = "athenz.zms.http_max_threads"; public static final String ZMS_PROP_AUTHORITY_CLASSES = "athenz.zms.authority_classes"; @@ -61,6 +62,18 @@ public final class ZMSConsts { public static final String ZMS_PROP_AUDIT_LOGGER_FACTORY_CLASS = "athenz.zms.audit_logger_factory_class"; public static final String ZMS_PROP_PRIVATE_KEY_STORE_FACTORY_CLASS = "athenz.zms.private_key_store_factory_class"; + // properties for our default quota limits + + public static final String ZMS_PROP_QUOTA_ROLE = "athenz.zms.quota_role"; + public static final String ZMS_PROP_QUOTA_ROLE_MEMBER = "athenz.zms.quota_role_member"; + public static final String ZMS_PROP_QUOTA_POLICY = "athenz.zms.quota_policy"; + public static final String ZMS_PROP_QUOTA_ASSERTION = "athenz.zms.quota_assertion"; + public static final String ZMS_PROP_QUOTA_SERVICE = "athenz.zms.quota_service"; + public static final String ZMS_PROP_QUOTA_SERVICE_HOST = "athenz.zms.quota_service_host"; + public static final String ZMS_PROP_QUOTA_PUBLIC_KEY = "athenz.zms.quota_public_key"; + public static final String ZMS_PROP_QUOTA_ENTITY = "athenz.zms.quota_entity"; + public static final String ZMS_PROP_QUOTA_SUBDOMAIN = "athenz.zms.quota_subdomain"; + public static final String ZMS_METRIC_FACTORY_CLASS = "com.yahoo.athenz.common.metrics.impl.NoOpMetricFactory"; public static final String ZMS_AUDIT_LOGGER_FACTORY_CLASS = "com.yahoo.athenz.common.server.log.impl.DefaultAuditLoggerFactory"; public static final String ZMS_PRINCIPAL_AUTHORITY_CLASS = "com.yahoo.athenz.auth.impl.PrincipalAuthority"; @@ -99,7 +112,15 @@ public final class ZMSConsts { public static final String DB_COLUMN_NAME = "name"; public static final String DB_COLUMN_TRUST = "trust"; public static final String DB_COLUMN_MEMBER = "member"; + public static final String DB_COLUMN_ENTITY = "entity"; + public static final String DB_COLUMN_SUBDOMAIN = "subdomain"; public static final String DB_COLUMN_ROLE = "role"; + public static final String DB_COLUMN_ROLE_MEMBER = "role_member"; + public static final String DB_COLUMN_POLICY = "policy"; + public static final String DB_COLUMN_SERVICE = "service"; + public static final String DB_COLUMN_SERVICE_HOST = "service_host"; + public static final String DB_COLUMN_PUBLIC_KEY = "public_key"; + public static final String DB_COLUMN_ASSERTION = "assertion"; public static final String DB_COLUMN_RESOURCE = "resource"; public static final String DB_COLUMN_ACTION = "action"; public static final String DB_COLUMN_EFFECT = "effect"; diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSHandler.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSHandler.java index e89183d2946..da73ba49133 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSHandler.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSHandler.java @@ -80,6 +80,9 @@ public interface ZMSHandler { public UserList getUserList(ResourceContext context); public UserMeta putUserMeta(ResourceContext context, String name, String auditRef, UserMeta detail); public User deleteUser(ResourceContext context, String name, String auditRef); + public Quota getQuota(ResourceContext context, String name); + public Quota putQuota(ResourceContext context, String name, String auditRef, Quota quota); + public Quota deleteQuota(ResourceContext context, String name, String auditRef); public Schema getRdlSchema(ResourceContext context); public ResourceContext newResourceContext(HttpServletRequest request, HttpServletResponse response); } 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 b5e1ab973ea..41ff5bc8bb2 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 @@ -133,6 +133,7 @@ public class ZMSImpl implements Authorizer, KeyStore, ZMSHandler { private static final String TYPE_PROVIDER_RESOURCE_GROUP_ROLES = "ProviderResourceGroupRoles"; private static final String TYPE_PUBLIC_KEY_ENTRY = "PublicKeyEntry"; private static final String TYPE_MEMBERSHIP = "Membership"; + private static final String TYPE_QUOTA = "Quota"; public static Metric metric; public static String serverHostName = null; @@ -365,6 +366,12 @@ void convertToLowerCase(Object obj) { DOMAIN_TEMPLATE_LIST.convertToLowerCase(domain.getTemplates()); } }, + QUOTA { + void convertToLowerCase(Object obj) { + Quota quota = (Quota) obj; + quota.setName(quota.getName().toLowerCase()); + } + }, USER_DOMAIN { void convertToLowerCase(Object obj) { UserDomain userDomain = (UserDomain) obj; @@ -540,10 +547,12 @@ void loadObjectStore() { String homeDir = System.getProperty(ZMSConsts.ZMS_PROP_FILE_STORE_PATH, getRootDir() + "/var/zms_server"); String fileDirName = System.getProperty(ZMSConsts.ZMS_PROP_FILE_STORE_NAME, "zms_root"); - String path = getFileStructPath(homeDir, fileDirName); - store = new FileObjectStore(new File(path)); + String quotaDirName = System.getProperty(ZMSConsts.ZMS_PROP_FILE_STORE_QUOTA, "zms_quota"); + String filePath = homeDir + File.separator + fileDirName; + String quotaPath = homeDir + File.separator + quotaDirName; + store = new FileObjectStore(new File(filePath), new File(quotaPath)); } - + dbService = new DBService(store, auditLogger, userDomain); } @@ -2650,6 +2659,106 @@ public Membership deleteMembership(ResourceContext ctx, String domainName, Strin return null; } + public Quota getQuota(ResourceContext ctx, String domainName) { + + final String caller = "getquota"; + metric.increment(ZMSConsts.HTTP_GET); + logPrincipal(ctx); + + validateRequest(ctx.request(), caller); + validate(domainName, TYPE_DOMAIN_NAME, caller); + + // for consistent handling of all requests, we're going to convert + // all incoming object values into lower case (e.g. domain, role, + // policy, service, etc name) + + domainName = domainName.toLowerCase(); + + metric.increment(ZMSConsts.HTTP_REQUEST, domainName); + metric.increment(caller, domainName); + Object timerMetric = metric.startTiming("getquota_timing", domainName); + + Quota result = dbService.getQuota(domainName); + + metric.stopTiming(timerMetric); + return result; + } + + public Quota putQuota(ResourceContext ctx, String domainName, String auditRef, Quota quota) { + + final String caller = "putQuota"; + metric.increment(ZMSConsts.HTTP_PUT); + logPrincipal(ctx); + + if (readOnlyMode) { + throw ZMSUtils.requestError("Server in Maintenance Read-Only mode. Please try your request later", caller); + } + + validateRequest(ctx.request(), caller); + + validate(domainName, TYPE_DOMAIN_NAME, caller); + validate(quota, TYPE_QUOTA, caller); + + // for consistent handling of all requests, we're going to convert + // all incoming object values into lower case (e.g. domain, role, + // policy, service, etc name) + + domainName = domainName.toLowerCase(); + AthenzObject.QUOTA.convertToLowerCase(quota); + + metric.increment(ZMSConsts.HTTP_REQUEST, domainName); + metric.increment(caller, domainName); + Object timerMetric = metric.startTiming("putquota_timing", domainName); + + // verify that request is properly authenticated for this request + + verifyAuthorizedServiceOperation(((RsrcCtxWrapper) ctx).principal().getAuthorizedService(), + caller); + + // verify that the domain name in the URI and object provided match + + if (!domainName.equals(quota.getName())) { + throw ZMSUtils.requestError("putQuota: Domain name in URI and Quota object do not match", caller); + } + + dbService.executePutQuota(ctx, domainName, quota, auditRef, caller); + metric.stopTiming(timerMetric); + return null; + } + + public Quota deleteQuota(ResourceContext ctx, String domainName, String auditRef) { + + final String caller = "deleteQuota"; + metric.increment(ZMSConsts.HTTP_DELETE); + logPrincipal(ctx); + + if (readOnlyMode) { + throw ZMSUtils.requestError("Server in Maintenance Read-Only mode. Please try your request later", caller); + } + + validateRequest(ctx.request(), caller); + + validate(domainName, TYPE_DOMAIN_NAME, caller); + + // for consistent handling of all requests, we're going to convert + // all incoming object values into lower case (e.g. domain, role, + // policy, service, etc name) + + domainName = domainName.toLowerCase(); + + metric.increment(ZMSConsts.HTTP_REQUEST, domainName); + metric.increment(caller, domainName); + Object timerMetric = metric.startTiming("deletequota_timing", domainName); + + // verify that request is properly authenticated for this request + + verifyAuthorizedServiceOperation(((RsrcCtxWrapper) ctx).principal().getAuthorizedService(), caller); + + dbService.executeDeleteQuota(ctx, domainName, auditRef, caller); + metric.stopTiming(timerMetric); + return null; + } + boolean hasExceededListLimit(Integer limit, int count) { if (limit == null) { @@ -6389,18 +6498,6 @@ Authority getAuthority(String className) { return authority; } - String getFileStructPath(String db_context, String name) { - - String path = db_context; - if (path == null) { - path = name; - } else if (name != null) { - path = path + File.separator + name; - } - - return path; - } - String getRootDir() { if (ROOT_DIR == null) { diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSResources.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSResources.java index 0c90c3a7b12..6bfd046e9d6 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSResources.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSResources.java @@ -32,6 +32,8 @@ public Domain getDomain(@PathParam("domain") String domain) { throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -52,6 +54,8 @@ public DomainList getDomainList(@QueryParam("limit") Integer limit, @QueryParam( } catch (ResourceException e) { int code = e.getCode(); switch (code) { + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -78,6 +82,8 @@ public Domain postTopLevelDomain(@HeaderParam("Y-Audit-Ref") String auditRef, To throw typedException(code, e, ResourceError.class); case ResourceException.FORBIDDEN: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -106,6 +112,8 @@ public Domain postSubDomain(@PathParam("parent") String parent, @HeaderParam("Y- throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -134,6 +142,8 @@ public Domain postUserDomain(@PathParam("name") String name, @HeaderParam("Y-Aud throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -161,6 +171,8 @@ public TopLevelDomain deleteTopLevelDomain(@PathParam("name") String name, @Head throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -188,6 +200,8 @@ public SubDomain deleteSubDomain(@PathParam("parent") String parent, @PathParam( throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -215,6 +229,8 @@ public UserDomain deleteUserDomain(@PathParam("name") String name, @HeaderParam( throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -245,6 +261,8 @@ public Domain putDomainMeta(@PathParam("name") String name, @HeaderParam("Y-Audi throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -275,6 +293,8 @@ public DomainTemplate putDomainTemplate(@PathParam("name") String name, @HeaderP throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -300,6 +320,8 @@ public DomainTemplateList getDomainTemplateList(@PathParam("name") String name) throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -329,6 +351,8 @@ public DomainTemplate deleteDomainTemplate(@PathParam("name") String name, @Path throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -354,6 +378,8 @@ public DomainDataCheck getDomainDataCheck(@PathParam("domainName") String domain throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -384,6 +410,8 @@ public Entity putEntity(@PathParam("domainName") String domainName, @PathParam(" throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -411,6 +439,8 @@ public Entity getEntity(@PathParam("domainName") String domainName, @PathParam(" throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -440,6 +470,8 @@ public Entity deleteEntity(@PathParam("domainName") String domainName, @PathPara throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -465,6 +497,8 @@ public EntityList getEntityList(@PathParam("domainName") String domainName) { throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -492,6 +526,8 @@ public RoleList getRoleList(@PathParam("domainName") String domainName, @QueryPa throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -517,6 +553,8 @@ public Roles getRoles(@PathParam("domainName") String domainName, @QueryParam("m throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -544,6 +582,8 @@ public Role getRole(@PathParam("domainName") String domainName, @PathParam("role throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -574,6 +614,8 @@ public Role putRole(@PathParam("domainName") String domainName, @PathParam("role throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -603,6 +645,8 @@ public Role deleteRole(@PathParam("domainName") String domainName, @PathParam("r throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -630,6 +674,8 @@ public Membership getMembership(@PathParam("domainName") String domainName, @Pat throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -660,6 +706,8 @@ public Membership putMembership(@PathParam("domainName") String domainName, @Pat throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -689,6 +737,8 @@ public Membership deleteMembership(@PathParam("domainName") String domainName, @ throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -717,6 +767,8 @@ public DefaultAdmins putDefaultAdmins(@PathParam("domainName") String domainName throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -744,6 +796,8 @@ public PolicyList getPolicyList(@PathParam("domainName") String domainName, @Que throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -769,6 +823,8 @@ public Policies getPolicies(@PathParam("domainName") String domainName, @QueryPa throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -796,6 +852,8 @@ public Policy getPolicy(@PathParam("domainName") String domainName, @PathParam(" throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -826,6 +884,8 @@ public Policy putPolicy(@PathParam("domainName") String domainName, @PathParam(" throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -855,6 +915,8 @@ public Policy deletePolicy(@PathParam("domainName") String domainName, @PathPara throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -882,6 +944,8 @@ public Assertion getAssertion(@PathParam("domainName") String domainName, @PathP throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -914,6 +978,8 @@ public Assertion putAssertion(@PathParam("domainName") String domainName, @PathP throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -943,6 +1009,8 @@ public Assertion deleteAssertion(@PathParam("domainName") String domainName, @Pa throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -973,6 +1041,8 @@ public ServiceIdentity putServiceIdentity(@PathParam("domain") String domain, @P throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1000,6 +1070,8 @@ public ServiceIdentity getServiceIdentity(@PathParam("domain") String domain, @P throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1029,6 +1101,8 @@ public ServiceIdentity deleteServiceIdentity(@PathParam("domain") String domain, throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1054,6 +1128,8 @@ public ServiceIdentities getServiceIdentities(@PathParam("domainName") String do throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1081,6 +1157,8 @@ public ServiceIdentityList getServiceIdentityList(@PathParam("domainName") Strin throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1108,6 +1186,8 @@ public PublicKeyEntry getPublicKeyEntry(@PathParam("domain") String domain, @Pat throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1138,6 +1218,8 @@ public PublicKeyEntry putPublicKeyEntry(@PathParam("domain") String domain, @Pat throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1167,6 +1249,8 @@ public PublicKeyEntry deletePublicKeyEntry(@PathParam("domain") String domain, @ throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1197,6 +1281,8 @@ public Tenancy putTenancy(@PathParam("domain") String domain, @PathParam("servic throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1224,6 +1310,8 @@ public Tenancy getTenancy(@PathParam("domain") String domain, @PathParam("servic throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1253,6 +1341,8 @@ public Tenancy deleteTenancy(@PathParam("domain") String domain, @PathParam("ser throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1283,6 +1373,8 @@ public TenancyResourceGroup putTenancyResourceGroup(@PathParam("domain") String throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1312,6 +1404,8 @@ public TenancyResourceGroup deleteTenancyResourceGroup(@PathParam("domain") Stri throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1344,6 +1438,8 @@ public TenantRoles putTenantRoles(@PathParam("domain") String domain, @PathParam throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1371,6 +1467,8 @@ public TenantRoles getTenantRoles(@PathParam("domain") String domain, @PathParam throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1400,6 +1498,8 @@ public TenantRoles deleteTenantRoles(@PathParam("domain") String domain, @PathPa throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1432,6 +1532,8 @@ public TenantResourceGroupRoles putTenantResourceGroupRoles(@PathParam("domain") throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1459,6 +1561,8 @@ public TenantResourceGroupRoles getTenantResourceGroupRoles(@PathParam("domain") throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1488,6 +1592,8 @@ public TenantResourceGroupRoles deleteTenantResourceGroupRoles(@PathParam("domai throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1520,6 +1626,8 @@ public ProviderResourceGroupRoles putProviderResourceGroupRoles(@PathParam("tena throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1547,6 +1655,8 @@ public ProviderResourceGroupRoles getProviderResourceGroupRoles(@PathParam("tena throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1576,6 +1686,8 @@ public ProviderResourceGroupRoles deleteProviderResourceGroupRoles(@PathParam("t throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1603,6 +1715,8 @@ public Access getAccess(@PathParam("action") String action, @PathParam("resource throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1630,6 +1744,8 @@ public Access getAccessExt(@PathParam("action") String action, @QueryParam("reso throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1657,6 +1773,8 @@ public ResourceAccessList getResourceAccessList(@QueryParam("principal") String throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1684,6 +1802,8 @@ public void getSignedDomains(@QueryParam("domain") String domain, @QueryParam("m throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1707,6 +1827,8 @@ public UserToken getUserToken(@PathParam("userName") String userName, @QueryPara switch (code) { case ResourceException.FORBIDDEN: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1728,6 +1850,8 @@ public UserToken optionsUserToken(@PathParam("userName") String userName, @Query switch (code) { case ResourceException.BAD_REQUEST: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); default: System.err.println("*** Warning: undeclared exception (" + code + ") for resource optionsUserToken"); throw typedException(code, e, ResourceError.class); @@ -1751,6 +1875,8 @@ public ServicePrincipal getServicePrincipal() { throw typedException(code, e, ResourceError.class); case ResourceException.FORBIDDEN: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1772,6 +1898,8 @@ public ServerTemplateList getServerTemplateList() { } catch (ResourceException e) { int code = e.getCode(); switch (code) { + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1797,6 +1925,8 @@ public Template getTemplate(@PathParam("template") String template) { throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1818,6 +1948,8 @@ public UserList getUserList() { } catch (ResourceException e) { int code = e.getCode(); switch (code) { + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1848,6 +1980,8 @@ public UserMeta putUserMeta(@PathParam("name") String name, @HeaderParam("Y-Audi throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1877,6 +2011,8 @@ public User deleteUser(@PathParam("name") String name, @HeaderParam("Y-Audit-Ref throw typedException(code, e, ResourceError.class); case ResourceException.NOT_FOUND: throw typedException(code, e, ResourceError.class); + case ResourceException.TOO_MANY_REQUESTS: + throw typedException(code, e, ResourceError.class); case ResourceException.UNAUTHORIZED: throw typedException(code, e, ResourceError.class); default: @@ -1886,6 +2022,88 @@ public User deleteUser(@PathParam("name") String name, @HeaderParam("Y-Audit-Ref } } + @GET + @Path("/domain/{name}/quota") + @Produces(MediaType.APPLICATION_JSON) + public Quota getQuota(@PathParam("name") String name) { + try { + ResourceContext context = this.delegate.newResourceContext(this.request, this.response); + context.authenticate(); + Quota e = this.delegate.getQuota(context, name); + return e; + } catch (ResourceException e) { + int code = e.getCode(); + switch (code) { + case ResourceException.BAD_REQUEST: + throw typedException(code, e, ResourceError.class); + case ResourceException.NOT_FOUND: + throw typedException(code, e, ResourceError.class); + default: + System.err.println("*** Warning: undeclared exception (" + code + ") for resource getQuota"); + throw typedException(code, e, ResourceError.class); + } + } + } + + @PUT + @Path("/domain/{name}/quota") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Quota putQuota(@PathParam("name") String name, @HeaderParam("Y-Audit-Ref") String auditRef, Quota quota) { + try { + ResourceContext context = this.delegate.newResourceContext(this.request, this.response); + context.authorize("update", "sys.auth:quota", null); + Quota e = this.delegate.putQuota(context, name, auditRef, quota); + return null; + } catch (ResourceException e) { + int code = e.getCode(); + switch (code) { + case ResourceException.BAD_REQUEST: + throw typedException(code, e, ResourceError.class); + case ResourceException.CONFLICT: + throw typedException(code, e, ResourceError.class); + case ResourceException.FORBIDDEN: + throw typedException(code, e, ResourceError.class); + case ResourceException.NOT_FOUND: + throw typedException(code, e, ResourceError.class); + case ResourceException.UNAUTHORIZED: + throw typedException(code, e, ResourceError.class); + default: + System.err.println("*** Warning: undeclared exception (" + code + ") for resource putQuota"); + throw typedException(code, e, ResourceError.class); + } + } + } + + @DELETE + @Path("/domain/{name}/quota") + @Produces(MediaType.APPLICATION_JSON) + public Quota deleteQuota(@PathParam("name") String name, @HeaderParam("Y-Audit-Ref") String auditRef) { + try { + ResourceContext context = this.delegate.newResourceContext(this.request, this.response); + context.authorize("update", "sys.auth:quota", null); + Quota e = this.delegate.deleteQuota(context, name, auditRef); + return null; + } catch (ResourceException e) { + int code = e.getCode(); + switch (code) { + case ResourceException.BAD_REQUEST: + throw typedException(code, e, ResourceError.class); + case ResourceException.CONFLICT: + throw typedException(code, e, ResourceError.class); + case ResourceException.FORBIDDEN: + throw typedException(code, e, ResourceError.class); + case ResourceException.NOT_FOUND: + throw typedException(code, e, ResourceError.class); + case ResourceException.UNAUTHORIZED: + throw typedException(code, e, ResourceError.class); + default: + System.err.println("*** Warning: undeclared exception (" + code + ") for resource deleteQuota"); + throw typedException(code, e, ResourceError.class); + } + } + } + @GET @Path("/schema") @Produces(MediaType.APPLICATION_JSON) diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/store/ObjectStoreConnection.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/store/ObjectStoreConnection.java index 93eb324feca..a05c6bf9c4c 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/store/ObjectStoreConnection.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/store/ObjectStoreConnection.java @@ -26,6 +26,7 @@ import com.yahoo.athenz.zms.Policy; import com.yahoo.athenz.zms.PrincipalRole; import com.yahoo.athenz.zms.PublicKeyEntry; +import com.yahoo.athenz.zms.Quota; import com.yahoo.athenz.zms.ResourceAccessList; import com.yahoo.athenz.zms.Role; import com.yahoo.athenz.zms.RoleAuditLog; @@ -91,13 +92,13 @@ public interface ObjectStoreConnection extends Closeable { boolean deletePolicy(String domainName, String policyName); List listPolicies(String domainName, String assertionRoleName); boolean updatePolicyModTimestamp(String domainName, String policyName); - + Assertion getAssertion(String domainName, String policyName, Long assertionId); boolean insertAssertion(String domainName, String policyName, Assertion assertion); boolean deleteAssertion(String domainName, String policyName, Long assertionId); List listAssertions(String domainName, String policyName); ResourceAccessList listResourceAccess(String principal, String action, String userDomain); - + // Service commands ServiceIdentity getServiceIdentity(String domainName, String serviceName); @@ -124,4 +125,11 @@ public interface ObjectStoreConnection extends Closeable { boolean updateEntity(String domainName, Entity entity); boolean deleteEntity(String domainName, String entityName); List listEntities(String domainName); + + // Quota commands + + Quota getQuota(String domainName); + boolean insertQuota(String domainName, Quota quota); + boolean updateQuota(String domainName, Quota quota); + boolean deleteQuota(String domainName); } diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/store/file/FileConnection.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/store/file/FileConnection.java index 6537603f157..d918706f147 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/store/file/FileConnection.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/store/file/FileConnection.java @@ -39,6 +39,7 @@ import com.yahoo.athenz.zms.Policy; import com.yahoo.athenz.zms.PrincipalRole; import com.yahoo.athenz.zms.PublicKeyEntry; +import com.yahoo.athenz.zms.Quota; import com.yahoo.athenz.zms.ResourceAccessList; import com.yahoo.athenz.zms.ResourceException; import com.yahoo.athenz.zms.Role; @@ -56,8 +57,10 @@ public class FileConnection implements ObjectStoreConnection { private static final String ALL_PRINCIPALS = "*"; File rootDir; - public FileConnection(File rootDir) { + File quotaDir; + public FileConnection(File rootDir, File quotaDir) { this.rootDir = rootDir; + this.quotaDir = quotaDir; } @Override @@ -1282,7 +1285,7 @@ public synchronized void putDomainStruct(String name, DomainStruct data) { } } - private static void delete(File f) { + private static boolean delete(File f) { if (f.exists()) { if (f.isDirectory()) { @@ -1296,16 +1299,18 @@ private static void delete(File f) { if (!f.delete()) { throw new RuntimeException("Cannot delete file: " + f); } + return true; } + return false; } public static void deleteDirectory(File f) { delete(f); } - public synchronized void delete(String name) { + public synchronized boolean delete(String name) { File f = new File(rootDir, name); - delete(f); + return delete(f); } String extractObjectName(String domainName, String fullName, String objType) { @@ -1379,4 +1384,49 @@ public boolean updatePolicyModTimestamp(String domainName, String policyName) { putDomainStruct(domainName, domainStruct); return true; } + + @Override + public Quota getQuota(String domainName) { + File f = new File(quotaDir, domainName); + if (!f.exists()) { + return null; + } + Quota quota = null; + try { + Path path = Paths.get(f.toURI()); + quota = JSON.fromBytes(Files.readAllBytes(path), Quota.class); + } catch (IOException e) { + } + return quota; + } + + boolean putQuota(String domainName, Quota quota) { + File f = new File(quotaDir, domainName); + String quotaData = JSON.string(quota); + try { + FileWriter fileWriter = new FileWriter(f); + fileWriter.write(quotaData); + fileWriter.flush(); + fileWriter.close(); + } catch (IOException e) { + return false; + } + return true; + } + + @Override + public boolean insertQuota(String domainName, Quota quota) { + return putQuota(domainName, quota); + } + + @Override + public boolean updateQuota(String domainName, Quota quota) { + return putQuota(domainName, quota); + } + + @Override + public boolean deleteQuota(String domainName) { + File quotaFile = new File(quotaDir, domainName); + return delete(quotaFile); + } } diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/store/file/FileObjectStore.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/store/file/FileObjectStore.java index 650cc6669d8..fed2a59251e 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/store/file/FileObjectStore.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/store/file/FileObjectStore.java @@ -23,23 +23,30 @@ public class FileObjectStore implements ObjectStore { File rootDir; + File quotaDir; - public FileObjectStore(File rootDirectory) { - if (!rootDirectory.exists()) { - if (!rootDirectory.mkdirs()) { - error("cannot create specified root: " + rootDirectory); + public FileObjectStore(File rootDirectory, File quotaDirectory) { + verifyDirectory(rootDirectory); + verifyDirectory(quotaDirectory); + this.rootDir = rootDirectory; + this.quotaDir = quotaDirectory; + } + + void verifyDirectory(File directory) { + if (!directory.exists()) { + if (!directory.mkdirs()) { + error("cannot create specified root: " + directory); } } else { - if (!rootDirectory.isDirectory()) { - error("specified root is not a directory: " + rootDirectory); + if (!directory.isDirectory()) { + error("specified root is not a directory: " + directory); } } - this.rootDir = rootDirectory; } - + @Override public ObjectStoreConnection getConnection(boolean autoCommit) { - return new FileConnection(rootDir); + return new FileConnection(rootDir, quotaDir); } @Override diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/store/jdbc/JDBCConnection.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/store/jdbc/JDBCConnection.java index 171db65bf06..bb97d8c355e 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/store/jdbc/JDBCConnection.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/store/jdbc/JDBCConnection.java @@ -44,6 +44,7 @@ import com.yahoo.athenz.zms.Policy; import com.yahoo.athenz.zms.PrincipalRole; import com.yahoo.athenz.zms.PublicKeyEntry; +import com.yahoo.athenz.zms.Quota; import com.yahoo.athenz.zms.ResourceAccess; import com.yahoo.athenz.zms.ResourceAccessList; import com.yahoo.athenz.zms.ResourceException; @@ -236,6 +237,15 @@ public class JDBCConnection implements ObjectStoreConnection { + "JOIN role ON role_member.role_id=role.role_id " + "JOIN domain ON domain.domain_id=role.domain_id " + "WHERE role_member.principal_id=?;"; + private static final String SQL_GET_QUOTA = "SELECT * FROM quota WHERE domain_id=?;"; + private static final String SQL_INSERT_QUOTA = "INSERT INTO quota (domain_id, role, role_member, " + + "policy, assertion, service, service_host, public_key, entity, subdomain) " + + "VALUES (?,?,?,?,?,?,?,?,?,?);"; + private static final String SQL_UPDATE_QUOTA = "UPDATE quota SET role=?, role_member=?, " + + "policy=?, assertion=?, service=?, service_host=?, public_key=?, entity=?, " + + "subdomain=? WHERE domain_id=?;"; + private static final String SQL_DELETE_QUOTA = "DELETE FROM quota WHERE domain_id=?;"; + private static final String CACHE_DOMAIN = "d:"; private static final String CACHE_ROLE = "r:"; @@ -2971,6 +2981,115 @@ public ResourceAccessList listResourceAccess(String principal, String action, St return rsrcAccessList; } + + @Override + public Quota getQuota(String domainName) { + + final String caller = "getQuota"; + + int domainId = getDomainId(con, domainName); + if (domainId == 0) { + throw notFoundError(caller, ZMSConsts.OBJECT_DOMAIN, domainName); + } + Quota quota = null; + try (PreparedStatement ps = con.prepareStatement(SQL_GET_QUOTA)) { + ps.setInt(1, domainId); + try (ResultSet rs = executeQuery(ps, caller)) { + if (rs.next()) { + quota = new Quota().setName(domainName); + quota.setAssertion(rs.getInt(ZMSConsts.DB_COLUMN_ASSERTION)); + quota.setRole(rs.getInt(ZMSConsts.DB_COLUMN_ROLE)); + quota.setRoleMember(rs.getInt(ZMSConsts.DB_COLUMN_ROLE_MEMBER)); + quota.setPolicy(rs.getInt(ZMSConsts.DB_COLUMN_POLICY)); + quota.setService(rs.getInt(ZMSConsts.DB_COLUMN_SERVICE)); + quota.setServiceHost(rs.getInt(ZMSConsts.DB_COLUMN_SERVICE_HOST)); + quota.setPublicKey(rs.getInt(ZMSConsts.DB_COLUMN_PUBLIC_KEY)); + quota.setEntity(rs.getInt(ZMSConsts.DB_COLUMN_ENTITY)); + quota.setSubdomain(rs.getInt(ZMSConsts.DB_COLUMN_SUBDOMAIN)); + quota.setModified(Timestamp.fromMillis(rs.getTimestamp(ZMSConsts.DB_COLUMN_MODIFIED).getTime())); + } + } + } catch (SQLException ex) { + throw sqlError(ex, caller); + } + return quota; + } + + @Override + public boolean insertQuota(String domainName, Quota quota) { + + final String caller = "insertQuota"; + + int domainId = getDomainId(con, domainName); + if (domainId == 0) { + throw notFoundError(caller, ZMSConsts.OBJECT_DOMAIN, domainName); + } + int affectedRows = 0; + try (PreparedStatement ps = con.prepareStatement(SQL_INSERT_QUOTA)) { + ps.setInt(1, domainId); + ps.setInt(2, quota.getRole()); + ps.setInt(3, quota.getRoleMember()); + ps.setInt(4, quota.getPolicy()); + ps.setInt(5, quota.getAssertion()); + ps.setInt(6, quota.getService()); + ps.setInt(7, quota.getServiceHost()); + ps.setInt(8, quota.getPublicKey()); + ps.setInt(9, quota.getEntity()); + ps.setInt(10, quota.getSubdomain()); + affectedRows = executeUpdate(ps, caller); + } catch (SQLException ex) { + throw sqlError(ex, caller); + } + return (affectedRows > 0); + } + + @Override + public boolean updateQuota(String domainName, Quota quota) { + + final String caller = "updateQuota"; + + int domainId = getDomainId(con, domainName); + if (domainId == 0) { + throw notFoundError(caller, ZMSConsts.OBJECT_DOMAIN, domainName); + } + int affectedRows = 0; + try (PreparedStatement ps = con.prepareStatement(SQL_UPDATE_QUOTA)) { + ps.setInt(1, quota.getRole()); + ps.setInt(2, quota.getRoleMember()); + ps.setInt(3, quota.getPolicy()); + ps.setInt(4, quota.getAssertion()); + ps.setInt(5, quota.getService()); + ps.setInt(6, quota.getServiceHost()); + ps.setInt(7, quota.getPublicKey()); + ps.setInt(8, quota.getEntity()); + ps.setInt(9, quota.getSubdomain()); + ps.setInt(10, domainId); + affectedRows = executeUpdate(ps, caller); + } catch (SQLException ex) { + throw sqlError(ex, caller); + } + return (affectedRows > 0); + } + + @Override + public boolean deleteQuota(String domainName) { + + final String caller = "deleteQuota"; + + int domainId = getDomainId(con, domainName); + if (domainId == 0) { + throw notFoundError(caller, ZMSConsts.OBJECT_DOMAIN, domainName); + } + int affectedRows = 0; + try (PreparedStatement ps = con.prepareStatement(SQL_DELETE_QUOTA)) { + ps.setInt(1, domainId); + affectedRows = executeUpdate(ps, caller); + } catch (SQLException ex) { + throw sqlError(ex, caller); + } + return (affectedRows > 0); + } + RuntimeException notFoundError(String caller, String objectType, String objectName) { rollbackChanges(); String message = "unknown " + objectType + " - " + objectName; diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/utils/ZMSUtils.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/utils/ZMSUtils.java index ef955dbfc56..8dea50bd3fd 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/utils/ZMSUtils.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/utils/ZMSUtils.java @@ -299,6 +299,10 @@ public static RuntimeException internalServerError(String msg, String caller) { return error(ResourceException.INTERNAL_SERVER_ERROR, msg, caller); } + public static RuntimeException quotaLimitError(String msg, String caller) { + return error(ResourceException.TOO_MANY_REQUESTS, msg, caller); + } + public static boolean emitMonmetricError(int errorCode, String caller) { if (errorCode < 1) { return false; 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 5c392376861..976619e7adf 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 @@ -72,6 +72,7 @@ public class DBServiceTest extends TestCase { @Mock HttpServletResponse mockServletResponse; private static final String ZMS_DATA_STORE_PATH = "/tmp/zms_core_unit_tests/zms_root"; + private static final String ZMS_DATA_QUOTA_PATH = "/tmp/zms_core_unit_tests/zms_quota"; static final Struct TABLE_PROVIDER_ROLE_ACTIONS = new Struct() .with("admin", "*").with("writer", "WRITE").with("reader", "READ"); @@ -143,8 +144,11 @@ Object getWebAppExcMapValue(javax.ws.rs.WebApplicationException wex, String head } private ZMSImpl zmsInit() { + // we want to make sure we start we clean dir structure + FileConnection.deleteDirectory(new File(ZMS_DATA_STORE_PATH)); + FileConnection.deleteDirectory(new File(ZMS_DATA_QUOTA_PATH)); Authority principalAuthority = new com.yahoo.athenz.common.server.debug.DebugPrincipalAuthority(); @@ -2274,7 +2278,7 @@ public void testInvalidDBServiceConfig() { @Test public void testShouldRetryOperation() { - FileObjectStore store = new FileObjectStore(new File(".")); + FileObjectStore store = new FileObjectStore(new File("."), new File(".")); DBService dbService = new DBService(store, null, "user"); // regardless of exception, count of 0 or 1 returns false @@ -3002,4 +3006,130 @@ public void testExecuteDeleteUserSubdomains() { zms.deleteTopLevelDomain(mockDomRsrcCtx, domainName, auditRef); zms.deleteTopLevelDomain(mockDomRsrcCtx, "deleteusersports", auditRef); } + + @Test + public void testExecutePutQuotaInsert() { + + String domainName = "executeputquotainsert"; + + TopLevelDomain dom1 = createTopLevelDomainObject(domainName, + "Test Domain1", "testOrg", adminUser); + zms.postTopLevelDomain(mockDomRsrcCtx, auditRef, dom1); + + Quota quota = new Quota().setName(domainName) + .setAssertion(10).setEntity(11) + .setPolicy(12).setPublicKey(13) + .setRole(14).setRoleMember(15) + .setService(16).setServiceHost(17) + .setSubdomain(18); + + zms.dbService.executePutQuota(mockDomRsrcCtx, domainName, quota, + auditRef, "testExecutePutQuotaInsert"); + + // now retrieve the quota using zms interface + + Quota quotaCheck = zms.getQuota(mockDomRsrcCtx, domainName); + assertNotNull(quotaCheck); + assertEquals(quotaCheck.getAssertion(), 10); + assertEquals(quotaCheck.getRole(), 14); + assertEquals(quotaCheck.getPolicy(), 12); + + zms.deleteTopLevelDomain(mockDomRsrcCtx, domainName, auditRef); + } + + @Test + public void testExecutePutQuotaUpdate() { + + String domainName = "executeputquotaupdate"; + + TopLevelDomain dom1 = createTopLevelDomainObject(domainName, + "Test Domain1", "testOrg", adminUser); + zms.postTopLevelDomain(mockDomRsrcCtx, auditRef, dom1); + + Quota quota = new Quota().setName(domainName) + .setAssertion(10).setEntity(11) + .setPolicy(12).setPublicKey(13) + .setRole(14).setRoleMember(15) + .setService(16).setServiceHost(17) + .setSubdomain(18); + + zms.dbService.executePutQuota(mockDomRsrcCtx, domainName, quota, + auditRef, "testExecutePutQuotaUpdate"); + + // now update the quota and apply the change again + + quota.setAssertion(100); + quota.setRole(104); + + zms.dbService.executePutQuota(mockDomRsrcCtx, domainName, quota, + auditRef, "testExecutePutQuotaUpdate"); + + // now retrieve the quota using zms interface + + Quota quotaCheck = zms.getQuota(mockDomRsrcCtx, domainName); + assertNotNull(quotaCheck); + assertEquals(quotaCheck.getAssertion(), 100); + assertEquals(quotaCheck.getRole(), 104); + assertEquals(quotaCheck.getPolicy(), 12); + + zms.deleteTopLevelDomain(mockDomRsrcCtx, domainName, auditRef); + } + + @Test + public void testExecuteDeleteQuota() { + + String domainName = "executedeletequota"; + + TopLevelDomain dom1 = createTopLevelDomainObject(domainName, + "Test Domain1", "testOrg", adminUser); + zms.postTopLevelDomain(mockDomRsrcCtx, auditRef, dom1); + + Quota quota = new Quota().setName(domainName) + .setAssertion(10).setEntity(11) + .setPolicy(12).setPublicKey(13) + .setRole(14).setRoleMember(15) + .setService(16).setServiceHost(17) + .setSubdomain(18); + + zms.dbService.executePutQuota(mockDomRsrcCtx, domainName, quota, + auditRef, "testExecuteDeleteQuota"); + + Quota quotaCheck = zms.getQuota(mockDomRsrcCtx, domainName); + assertNotNull(quotaCheck); + assertEquals(domainName, quotaCheck.getName()); + assertEquals(quotaCheck.getAssertion(), 10); + assertEquals(quotaCheck.getRole(), 14); + assertEquals(quotaCheck.getPolicy(), 12); + + // now delete the quota + + zms.dbService.executeDeleteQuota(mockDomRsrcCtx, domainName, auditRef, + "testExecuteDeleteQuota"); + + // now we'll get the default quota + + quotaCheck = zms.getQuota(mockDomRsrcCtx, domainName); + + assertEquals("server-default", quotaCheck.getName()); + assertEquals(quotaCheck.getAssertion(), 100); + assertEquals(quotaCheck.getRole(), 1000); + assertEquals(quotaCheck.getPolicy(), 1000); + + zms.deleteTopLevelDomain(mockDomRsrcCtx, domainName, auditRef); + } + + @Test + public void testExecuteDeleteQuotaException() { + + String domainName = "executedeletequotaexception"; + + // delete the quota for nonexistent domain + + try { + zms.dbService.executeDeleteQuota(mockDomRsrcCtx, domainName, auditRef, + "testExecuteDeleteQuota"); + fail(); + } catch (ResourceException ex) { + } + } } diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/QuotaCheckerTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/QuotaCheckerTest.java new file mode 100644 index 00000000000..d7a3f5d2e15 --- /dev/null +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/QuotaCheckerTest.java @@ -0,0 +1,592 @@ +package com.yahoo.athenz.zms; + +import java.util.ArrayList; + +import org.mockito.Mockito; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +import com.yahoo.athenz.zms.store.ObjectStoreConnection; + +public class QuotaCheckerTest { + + @Test + public void testGetDomainQuota() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setAssertion(10).setEntity(11) + .setPolicy(12).setPublicKey(13) + .setRole(14).setRoleMember(15) + .setService(16).setServiceHost(17) + .setSubdomain(18); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + + Quota quota = quotaCheck.getDomainQuota(con, "athenz"); + assertNotNull(quota); + assertEquals(quota.getAssertion(), 10); + assertEquals(quota.getRole(), 14); + assertEquals(quota.getPolicy(), 12); + } + + @Test + public void testGetDomainQuotaDefault() { + + QuotaChecker quotaCheck = new QuotaChecker(); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(null); + + // we should get back our default quota + + Quota quota = quotaCheck.getDomainQuota(con, "athenz"); + assertNotNull(quota); + assertEquals(quota.getAssertion(), 100); + assertEquals(quota.getRole(), 1000); + assertEquals(quota.getPolicy(), 1000); + } + + @Test + public void testGetListSize() { + + QuotaChecker quotaCheck = new QuotaChecker(); + assertEquals(quotaCheck.getListSize(null), 0); + + ArrayList list = new ArrayList<>(); + assertEquals(quotaCheck.getListSize(list), 0); + + list.add("test1"); + assertEquals(quotaCheck.getListSize(list), 1); + + list.add("test2"); + list.add("test3"); + assertEquals(quotaCheck.getListSize(list), 3); + } + + @Test + public void testCheckSubDomainQuotaTopLevel() { + + // top level domains have no check + QuotaChecker quotaCheck = new QuotaChecker(); + quotaCheck.checkSubdomainQuota(null, "athenz", "caller"); + } + + @Test + public void testCheckSubDomainQuota() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setSubdomain(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + ArrayList domains = new ArrayList<>(); + domains.add("athenz.one"); + Mockito.when(con.listDomains("athenz.", 0)).thenReturn(domains); + + // this should be successful - no exceptions + + quotaCheck.checkSubdomainQuota(con, "athenz.quota", "caller"); + } + + @Test + public void testCheckSubDomainQuotaExceeded() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setSubdomain(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + ArrayList domains = new ArrayList<>(); + domains.add("athenz.one"); + domains.add("athenz.two"); + Mockito.when(con.listDomains("athenz.", 0)).thenReturn(domains); + + // this should be successful - no exceptions + + try { + quotaCheck.checkSubdomainQuota(con, "athenz.quota", "caller"); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.TOO_MANY_REQUESTS); + } + } + + @Test + public void testCheckRoleQuotaNull() { + + // null objects have no check + QuotaChecker quotaCheck = new QuotaChecker(); + quotaCheck.checkRoleQuota(null, "athenz", null, "caller"); + } + + @Test + public void testCheckRoleQuota() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setRole(2).setRoleMember(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + ArrayList roles = new ArrayList<>(); + roles.add("athenz.one"); + Mockito.when(con.listRoles("athenz")).thenReturn(roles); + + ArrayList roleMembers = new ArrayList<>(); + roleMembers.add(new RoleMember().setMemberName("user.joe")); + Role role = new Role().setRoleMembers(roleMembers); + + // this should be successful - no exceptions + + quotaCheck.checkRoleQuota(con, "athenz", role, "caller"); + } + + @Test + public void testCheckRoleQuotaRoleMemberExceeded() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setRole(2).setRoleMember(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + ArrayList roles = new ArrayList<>(); + roles.add("athenz.one"); + Mockito.when(con.listRoles("athenz")).thenReturn(roles); + + ArrayList roleMembers = new ArrayList<>(); + roleMembers.add(new RoleMember().setMemberName("user.joe")); + roleMembers.add(new RoleMember().setMemberName("user.jane")); + roleMembers.add(new RoleMember().setMemberName("user.doe")); + Role role = new Role().setRoleMembers(roleMembers); + + try { + quotaCheck.checkRoleQuota(con, "athenz", role, "caller"); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.TOO_MANY_REQUESTS); + assertTrue(ex.getMessage().contains("role member quota exceeded")); + } + } + + @Test + public void testCheckRoleQuotaRoleCountExceeded() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setRole(2).setRoleMember(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + ArrayList roles = new ArrayList<>(); + roles.add("athenz.one"); + roles.add("athenz.two"); + Mockito.when(con.listRoles("athenz")).thenReturn(roles); + + ArrayList roleMembers = new ArrayList<>(); + roleMembers.add(new RoleMember().setMemberName("user.joe")); + Role role = new Role().setRoleMembers(roleMembers); + + try { + quotaCheck.checkRoleQuota(con, "athenz", role, "caller"); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.TOO_MANY_REQUESTS); + assertTrue(ex.getMessage().contains("role quota exceeded")); + } + } + + @Test + public void testCheckRoleMembershipQuota() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setRole(2).setRoleMember(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + + ArrayList roleMembers = new ArrayList<>(); + roleMembers.add(new RoleMember().setMemberName("user.joe")); + + // this should complete successfully + + quotaCheck.checkRoleMembershipQuota(con, "athenz", roleMembers, "caller"); + } + + @Test + public void testCheckRoleMembershipQuotaRoleCountExceeded() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setRole(2).setRoleMember(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + + ArrayList roleMembers = new ArrayList<>(); + roleMembers.add(new RoleMember().setMemberName("user.joe")); + roleMembers.add(new RoleMember().setMemberName("user.jane")); + + try { + quotaCheck.checkRoleMembershipQuota(con, "athenz", roleMembers, "caller"); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.TOO_MANY_REQUESTS); + assertTrue(ex.getMessage().contains("role member quota exceeded")); + } + } + + @Test + public void testCheckPolicyQuotaNull() { + + // null objects have no check + QuotaChecker quotaCheck = new QuotaChecker(); + quotaCheck.checkPolicyQuota(null, "athenz", null, "caller"); + } + + @Test + public void testCheckPolicyQuota() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setPolicy(2).setAssertion(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + ArrayList policies = new ArrayList<>(); + policies.add("athenz.one"); + Mockito.when(con.listPolicies("athenz", null)).thenReturn(policies); + + ArrayList assertions = new ArrayList<>(); + assertions.add(new Assertion().setAction("*").setResource("*").setRole("admin")); + + Policy policy = new Policy(); + policy.setAssertions(assertions); + + // this should be successful - no exceptions + + quotaCheck.checkPolicyQuota(con, "athenz", policy, "caller"); + } + + @Test + public void testCheckPolicyQuotaAssertionExceeded() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setPolicy(2).setAssertion(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + ArrayList policies = new ArrayList<>(); + policies.add("athenz.one"); + Mockito.when(con.listPolicies("athenz", null)).thenReturn(policies); + + ArrayList assertions = new ArrayList<>(); + assertions.add(new Assertion().setAction("*").setResource("*").setRole("admin")); + assertions.add(new Assertion().setAction("*").setResource("*").setRole("admin")); + assertions.add(new Assertion().setAction("*").setResource("*").setRole("admin")); + + Policy policy = new Policy(); + policy.setAssertions(assertions); + + try { + quotaCheck.checkPolicyQuota(con, "athenz", policy, "caller"); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.TOO_MANY_REQUESTS); + assertTrue(ex.getMessage().contains("policy assertion quota exceeded")); + } + } + + @Test + public void testCheckPolicyQuotaPolicyCountExceeded() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setPolicy(2).setAssertion(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + ArrayList policies = new ArrayList<>(); + policies.add("athenz.one"); + policies.add("athenz.two"); + Mockito.when(con.listPolicies("athenz", null)).thenReturn(policies); + + ArrayList assertions = new ArrayList<>(); + assertions.add(new Assertion().setAction("*").setResource("*").setRole("admin")); + assertions.add(new Assertion().setAction("*").setResource("*").setRole("admin")); + + Policy policy = new Policy(); + policy.setAssertions(assertions); + + try { + quotaCheck.checkPolicyQuota(con, "athenz", policy, "caller"); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.TOO_MANY_REQUESTS); + assertTrue(ex.getMessage().contains("policy quota exceeded")); + } + } + + @Test + public void testCheckPolicyAssertionQuota() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setPolicy(2).setAssertion(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + + ArrayList assertions = new ArrayList<>(); + assertions.add(new Assertion().setAction("*").setResource("*").setRole("admin")); + + // this should be successful - no exceptions + + quotaCheck.checkPolicyAssertionQuota(con, "athenz", assertions, "caller"); + } + + @Test + public void testCheckPolicyAssertionQuotaAssertionExceeded() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setPolicy(2).setAssertion(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + + ArrayList assertions = new ArrayList<>(); + assertions.add(new Assertion().setAction("*").setResource("*").setRole("admin")); + assertions.add(new Assertion().setAction("*").setResource("*").setRole("admin")); + assertions.add(new Assertion().setAction("*").setResource("*").setRole("admin")); + + try { + quotaCheck.checkPolicyAssertionQuota(con, "athenz", assertions, "caller"); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.TOO_MANY_REQUESTS); + assertTrue(ex.getMessage().contains("policy assertion quota exceeded")); + } + } + + @Test + public void testCheckServiceQuotaNull() { + + // null objects have no check + QuotaChecker quotaCheck = new QuotaChecker(); + quotaCheck.checkServiceIdentityQuota(null, "athenz", null, "caller"); + } + + @Test + public void testCheckServiceQuota() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setService(2).setServiceHost(2).setPublicKey(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + ArrayList services = new ArrayList<>(); + services.add("athenz.one"); + Mockito.when(con.listServiceIdentities("athenz")).thenReturn(services); + + ArrayList hosts = new ArrayList<>(); + hosts.add("host1"); + + ArrayList publicKeys = new ArrayList<>(); + publicKeys.add(new PublicKeyEntry().setId("id1").setKey("key")); + + ServiceIdentity service = new ServiceIdentity(); + service.setHosts(hosts); + service.setPublicKeys(publicKeys); + + // this should be successful - no exceptions + + quotaCheck.checkServiceIdentityQuota(con, "athenz", service, "caller"); + } + + @Test + public void testCheckServiceQuotaServiceCountExceeded() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setService(2).setServiceHost(2).setPublicKey(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + ArrayList services = new ArrayList<>(); + services.add("athenz.one"); + services.add("athenz.two"); + Mockito.when(con.listServiceIdentities("athenz")).thenReturn(services); + + ArrayList hosts = new ArrayList<>(); + hosts.add("host1"); + + ArrayList publicKeys = new ArrayList<>(); + publicKeys.add(new PublicKeyEntry().setId("id1").setKey("key")); + + ServiceIdentity service = new ServiceIdentity(); + service.setHosts(hosts); + service.setPublicKeys(publicKeys); + + // this should be successful - no exceptions + + try { + quotaCheck.checkServiceIdentityQuota(con, "athenz", service, "caller"); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.TOO_MANY_REQUESTS); + assertTrue(ex.getMessage().contains("service quota exceeded")); + } + } + + @Test + public void testCheckServiceQuotaServiceHostExceeded() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setService(2).setServiceHost(2).setPublicKey(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + ArrayList services = new ArrayList<>(); + services.add("athenz.one"); + Mockito.when(con.listServiceIdentities("athenz")).thenReturn(services); + + ArrayList hosts = new ArrayList<>(); + hosts.add("host1"); + hosts.add("host2"); + hosts.add("host3"); + + ArrayList publicKeys = new ArrayList<>(); + publicKeys.add(new PublicKeyEntry().setId("id1").setKey("key")); + + ServiceIdentity service = new ServiceIdentity(); + service.setHosts(hosts); + service.setPublicKeys(publicKeys); + + // this should be successful - no exceptions + + try { + quotaCheck.checkServiceIdentityQuota(con, "athenz", service, "caller"); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.TOO_MANY_REQUESTS); + assertTrue(ex.getMessage().contains("service host quota exceeded")); + } + } + + @Test + public void testCheckServiceQuotaPublicKeyExceeded() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setService(2).setServiceHost(2).setPublicKey(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + ArrayList services = new ArrayList<>(); + services.add("athenz.one"); + Mockito.when(con.listServiceIdentities("athenz")).thenReturn(services); + + ArrayList hosts = new ArrayList<>(); + hosts.add("host1"); + + ArrayList publicKeys = new ArrayList<>(); + publicKeys.add(new PublicKeyEntry().setId("id1").setKey("key")); + publicKeys.add(new PublicKeyEntry().setId("id2").setKey("key")); + publicKeys.add(new PublicKeyEntry().setId("id3").setKey("key")); + + ServiceIdentity service = new ServiceIdentity(); + service.setHosts(hosts); + service.setPublicKeys(publicKeys); + + // this should be successful - no exceptions + + try { + quotaCheck.checkServiceIdentityQuota(con, "athenz", service, "caller"); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.TOO_MANY_REQUESTS); + assertTrue(ex.getMessage().contains("service public key quota exceeded")); + } + } + + @Test + public void testCheckServicePublicKeyQuota() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setService(2).setServiceHost(2).setPublicKey(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + + ArrayList publicKeys = new ArrayList<>(); + publicKeys.add(new PublicKeyEntry().setId("id1").setKey("key")); + + // this should be successful - no exceptions + + quotaCheck.checkServiceIdentityPublicKeyQuota(con, "athenz", publicKeys, "caller"); + } + + @Test + public void testCheckServicePublicKeyQuotaExceeded() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setService(2).setServiceHost(2).setPublicKey(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + + ArrayList publicKeys = new ArrayList<>(); + publicKeys.add(new PublicKeyEntry().setId("id1").setKey("key")); + publicKeys.add(new PublicKeyEntry().setId("id2").setKey("key")); + + try { + quotaCheck.checkServiceIdentityPublicKeyQuota(con, "athenz", publicKeys, "caller"); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.TOO_MANY_REQUESTS); + assertTrue(ex.getMessage().contains("service public key quota exceeded")); + } + } + + @Test + public void testCheckEntityQuotaNull() { + + // null objects have no check + QuotaChecker quotaCheck = new QuotaChecker(); + quotaCheck.checkEntityQuota(null, "athenz", null, "caller"); + } + + @Test + public void testCheckEntityQuota() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setEntity(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + ArrayList entities = new ArrayList<>(); + entities.add("athenz.one"); + Mockito.when(con.listEntities("athenz")).thenReturn(entities); + + Entity entity = new Entity(); + + // this should be successful - no exceptions + + quotaCheck.checkEntityQuota(con, "athenz", entity, "caller"); + } + + @Test + public void testCheckEntityQuotaEntityCountExceeded() { + + QuotaChecker quotaCheck = new QuotaChecker(); + Quota mockQuota = new Quota().setName("athenz") + .setEntity(2); + ObjectStoreConnection con = Mockito.mock(ObjectStoreConnection.class); + Mockito.when(con.getQuota("athenz")).thenReturn(mockQuota); + ArrayList entities = new ArrayList<>(); + entities.add("athenz.one"); + entities.add("athenz.two"); + Mockito.when(con.listEntities("athenz")).thenReturn(entities); + + Entity entity = new Entity(); + + try { + quotaCheck.checkEntityQuota(con, "athenz", entity, "caller"); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.TOO_MANY_REQUESTS); + assertTrue(ex.getMessage().contains("entity quota exceeded")); + } + } +} diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSBinderTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSBinderTest.java new file mode 100644 index 00000000000..a1fac99993a --- /dev/null +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSBinderTest.java @@ -0,0 +1,17 @@ +package com.yahoo.athenz.zms; + +import org.testng.annotations.Test; + +public class ZMSBinderTest { + + @Test + public void testZMSBinder() { + + System.setProperty(ZMSConsts.ZMS_PROP_FILE_NAME, "src/test/resources/zms.properties"); + System.setProperty(ZMSConsts.ZMS_PROP_FILE_STORE_PATH, "/tmp/zms_core_unit_tests/"); + System.setProperty(ZMSConsts.ZMS_PROP_DOMAIN_ADMIN, "user.testadminuser"); + + ZMSBinder binder = new ZMSBinder(); + binder.configure(); + } +} 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 9714943db76..8d009275a77 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 @@ -10832,6 +10832,13 @@ public void testConvertToLowerCasePublicKeyEntry() { assertEquals(keyEntry.getId(), "zone1"); } + @Test + public void testConvertToLowerCaseQuota() { + Quota quota = new Quota().setName("UpperCaseDomain"); + AthenzObject.QUOTA.convertToLowerCase(quota); + assertEquals(quota.getName(), "uppercasedomain"); + } + @Test public void testConvertToLowerCaseEntity() { Entity entity = createEntityObject("ABcEntity"); @@ -14510,5 +14517,100 @@ public void testDeleteUser() { zms.deleteTopLevelDomain(mockDomRsrcCtx, domainName, auditRef); zms.deleteTopLevelDomain(mockDomRsrcCtx, "deleteusersports", auditRef); } + + @Test + public void testPutQuota() { + + String domainName = "putquota"; + + TopLevelDomain dom1 = createTopLevelDomainObject(domainName, + "Test Domain1", "testOrg", adminUser); + zms.postTopLevelDomain(mockDomRsrcCtx, auditRef, dom1); + + Quota quota = new Quota().setName(domainName) + .setAssertion(10).setEntity(11) + .setPolicy(12).setPublicKey(13) + .setRole(14).setRoleMember(15) + .setService(16).setServiceHost(17) + .setSubdomain(18); + + zms.putQuota(mockDomRsrcCtx, domainName, auditRef, quota); + + // now retrieve the quota using zms interface + + Quota quotaCheck = zms.getQuota(mockDomRsrcCtx, domainName); + assertNotNull(quotaCheck); + assertEquals(quotaCheck.getAssertion(), 10); + assertEquals(quotaCheck.getRole(), 14); + assertEquals(quotaCheck.getPolicy(), 12); + + zms.deleteTopLevelDomain(mockDomRsrcCtx, domainName, auditRef); + } + + @Test + public void testPutQuotaMismatchName() { + + String domainName = "putquotamismatchname"; + + TopLevelDomain dom1 = createTopLevelDomainObject(domainName, + "Test Domain1", "testOrg", adminUser); + zms.postTopLevelDomain(mockDomRsrcCtx, auditRef, dom1); + + Quota quota = new Quota().setName("athenz") + .setAssertion(10).setEntity(11) + .setPolicy(12).setPublicKey(13) + .setRole(14).setRoleMember(15) + .setService(16).setServiceHost(17) + .setSubdomain(18); + + try { + zms.putQuota(mockDomRsrcCtx, domainName, auditRef, quota); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); + } + + zms.deleteTopLevelDomain(mockDomRsrcCtx, domainName, auditRef); + } + + @Test + public void testDeleteQuota() { + + String domainName = "deletequota"; + + TopLevelDomain dom1 = createTopLevelDomainObject(domainName, + "Test Domain1", "testOrg", adminUser); + zms.postTopLevelDomain(mockDomRsrcCtx, auditRef, dom1); + + Quota quota = new Quota().setName(domainName) + .setAssertion(10).setEntity(11) + .setPolicy(12).setPublicKey(13) + .setRole(14).setRoleMember(15) + .setService(16).setServiceHost(17) + .setSubdomain(18); + + zms.putQuota(mockDomRsrcCtx, domainName, auditRef, quota); + + Quota quotaCheck = zms.getQuota(mockDomRsrcCtx, domainName); + assertNotNull(quotaCheck); + assertEquals(domainName, quotaCheck.getName()); + assertEquals(quotaCheck.getAssertion(), 10); + assertEquals(quotaCheck.getRole(), 14); + assertEquals(quotaCheck.getPolicy(), 12); + + // now delete the quota + + zms.deleteQuota(mockDomRsrcCtx, domainName, auditRef); + + // now we'll get the default quota + + quotaCheck = zms.getQuota(mockDomRsrcCtx, domainName); + + assertEquals("server-default", quotaCheck.getName()); + assertEquals(quotaCheck.getAssertion(), 100); + assertEquals(quotaCheck.getRole(), 1000); + assertEquals(quotaCheck.getPolicy(), 1000); + + zms.deleteTopLevelDomain(mockDomRsrcCtx, domainName, auditRef); + } } diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/store/file/FileConnectionTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/store/file/FileConnectionTest.java index 06a4d80f086..5e0afff7e24 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/store/file/FileConnectionTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/store/file/FileConnectionTest.java @@ -34,16 +34,18 @@ public class FileConnectionTest { @Test public void testGetDomainModTimestamp() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { fileconnection.getDomainModTimestamp("DummyDomain1"); } } @Test public void testUpdateDomain() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { Domain domMock = Mockito.mock(Domain.class); Mockito.when(domMock.getName()).thenReturn("domain1"); @@ -56,16 +58,18 @@ public void testUpdateDomain() { @Test public void testRemovePublicKeyEntry() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { assertFalse(fileconnection.removePublicKeyEntry(null, "12")); } } @Test public void testLookupDomainByRole() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.lookupDomainByRole("member1", "role1"); } catch (Exception ex) { @@ -76,8 +80,9 @@ public void testLookupDomainByRole() { @Test public void testValidatePrincipalDomain() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.validatePrincipalDomain("principal"); } catch (Exception ex) { @@ -88,8 +93,9 @@ public void testValidatePrincipalDomain() { @Test public void testUpdateDomainModTimestamp() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.updateDomainModTimestamp("DummyDomain1"); } catch (Exception ex) { @@ -100,8 +106,9 @@ public void testUpdateDomainModTimestamp() { @Test public void testInsertDomainTemplate() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.insertDomainTemplate("DummyDomain1", "Template1", "param"); } catch (Exception ex) { @@ -112,8 +119,9 @@ public void testInsertDomainTemplate() { @Test public void testDeleteDomainTemplate() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.deleteDomainTemplate("DummyDomain1", "Template1", "param"); } catch (Exception ex) { @@ -124,8 +132,9 @@ public void testDeleteDomainTemplate() { @Test public void testListEntities() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.listEntities("DummyDomain1"); } catch (Exception ex) { @@ -136,8 +145,9 @@ public void testListEntities() { @Test public void testListRoleMembers() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.listRoleMembers("DummyDomain1", "Role1"); } catch (Exception ex) { @@ -148,8 +158,9 @@ public void testListRoleMembers() { @Test public void testInsertRoleMember() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.insertRoleMember("DummyDomain1", "Role1", new RoleMember().setMemberName("principal1"), "audit1", "zmsjcltest"); @@ -161,8 +172,9 @@ public void testInsertRoleMember() { @Test public void testUpdatePolicy() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { Policy policy = new Policy(); try { fileconnection.updatePolicy("Domain1", policy); @@ -174,8 +186,9 @@ public void testUpdatePolicy() { @Test public void testDeletePolicy() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.deletePolicy("Domain1", "policy1"); } catch (Exception ex) { @@ -186,8 +199,9 @@ public void testDeletePolicy() { @Test public void testInsertAssertion() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { Assertion assertion = new Assertion(); try { fileconnection.insertAssertion("Domain1", "policy1", assertion); @@ -199,8 +213,9 @@ public void testInsertAssertion() { @Test public void testListAssertions() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.listAssertions("Domain1", "Policy1"); } catch (Exception ex) { @@ -211,8 +226,9 @@ public void testListAssertions() { @Test public void testUpdateServiceIdentity() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { ServiceIdentity service1 = new ServiceIdentity(); try { fileconnection.updateServiceIdentity("Domain1", service1); @@ -224,8 +240,9 @@ public void testUpdateServiceIdentity() { @Test public void testDeleteServiceIdentity() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.deleteServiceIdentity("Domain1", "service1"); } catch (Exception ex) { @@ -236,8 +253,9 @@ public void testDeleteServiceIdentity() { @Test public void testListPublicKeys() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.listPublicKeys("Domain1", "service1"); } catch (Exception ex) { @@ -248,8 +266,9 @@ public void testListPublicKeys() { @Test public void testListServiceHosts() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.listPublicKeys("Domain1", "service1"); } catch (Exception ex) { @@ -260,8 +279,9 @@ public void testListServiceHosts() { @Test public void testInsertServiceHost() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.insertServiceHost("Domain1", "service1", "athenz.zms.client.zms_url"); } catch (Exception ex) { @@ -272,8 +292,9 @@ public void testInsertServiceHost() { @Test public void testDeleteServiceHost() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.deleteServiceHost("Domain1", "service1", "athenz.zms.client.zms_url"); } catch (Exception ex) { @@ -284,8 +305,9 @@ public void testDeleteServiceHost() { @Test public void testUpdateEntity() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { Entity entity1 = new Entity(); try { fileconnection.updateEntity("Domain1", entity1); @@ -297,8 +319,9 @@ public void testUpdateEntity() { @Test public void testDeleteEntity() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.deleteEntity("Domain1", "entity1"); } catch (Exception ex) { @@ -309,16 +332,18 @@ public void testDeleteEntity() { @Test public void testDelete() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { fileconnection.delete("zms"); } } @Test public void testListResourceAccess() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.listResourceAccess("principal1", "UPDATE", "UserDomain1"); } catch (Exception ex) { @@ -329,8 +354,9 @@ public void testListResourceAccess() { @Test public void testUpdatePolicyModTimestamp() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.updatePolicyModTimestamp("domain1", "policy1"); } catch (Exception ex) { @@ -341,8 +367,9 @@ public void testUpdatePolicyModTimestamp() { @Test public void testUpdateRole() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { Role role = new Role(); try { fileconnection.updateRole("domain1", role); @@ -354,8 +381,9 @@ public void testUpdateRole() { @Test public void testUpdateRoleModTimestamp() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.updateRoleModTimestamp("domain1", "role1"); } catch (Exception ex) { @@ -366,8 +394,9 @@ public void testUpdateRoleModTimestamp() { @Test public void testDeleteRole() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.deleteRole("domain1", "role1"); } catch (Exception ex) { @@ -378,8 +407,9 @@ public void testDeleteRole() { @Test public void testListServiceHostsList() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.listServiceHosts("domain1", "service1"); } catch (Exception ex) { @@ -390,8 +420,9 @@ public void testListServiceHostsList() { @Test public void testGetRoleObject() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { DomainStruct domain = new DomainStruct(); fileconnection.getRoleObject(domain, "role1"); } @@ -399,8 +430,9 @@ public void testGetRoleObject() { @Test public void testGetPolicyObject() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { DomainStruct domain = new DomainStruct(); fileconnection.getPolicyObject(domain, "role1"); } @@ -408,8 +440,9 @@ public void testGetPolicyObject() { @Test public void testDeleteRoleMember() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.deleteRoleMember("domain1", "role1", "principal", "admin", "zmsjcltest"); } catch (Exception ex) { @@ -420,8 +453,9 @@ public void testDeleteRoleMember() { @Test public void testGetAssertion() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { long assertid = 33456; try { fileconnection.getAssertion("domain1", "policy1", assertid); @@ -433,8 +467,9 @@ public void testGetAssertion() { @Test public void testDeleteAssertion() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { long assertid = 33456; try { fileconnection.deleteAssertion("domain1", "policy1", assertid); @@ -446,8 +481,9 @@ public void testDeleteAssertion() { @Test public void testDeletePublicKeyEntry() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { try { fileconnection.deletePublicKeyEntry("domain1", "service1", "223"); } catch (Exception ex) { @@ -458,8 +494,9 @@ public void testDeletePublicKeyEntry() { @Test public void testUpdatePublicKeyEntry() { - File file = new File("/home/athenz/"); - try (FileConnection fileconnection = new FileConnection(file)) { + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { PublicKeyEntry keyEntry = new PublicKeyEntry(); try { fileconnection.updatePublicKeyEntry("domain1", "service1", keyEntry); @@ -471,12 +508,13 @@ public void testUpdatePublicKeyEntry() { @Test public void testAssertionMatch() { - File file = new File("/home/athenz/"); + File fileDir = new File("/home/athenz/zms_store"); + File quotaDir = new File("/home/athenz/zms_quota"); Assertion assertion1 = new Assertion(); assertion1.setAction("UPDATE").setResource("resource").setRole("zmsRole"); Assertion assertion2 = new Assertion(); assertion2.setAction("UPDATE").setResource("resource").setRole("zmsRole"); - try (FileConnection fileconnection = new FileConnection(file)) { + try (FileConnection fileconnection = new FileConnection(fileDir, quotaDir)) { assertTrue(fileconnection.assertionMatch(assertion1, assertion2)); Assertion assertion3 = new Assertion(); diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/store/file/FileObjectStoreTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/store/file/FileObjectStoreTest.java index afcaea0dee7..6b42666bf56 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/store/file/FileObjectStoreTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/store/file/FileObjectStoreTest.java @@ -31,19 +31,15 @@ public void testError() { } @Test - public void TestFileObjectStore() { - File file = new File("/home/athenz/"); - File file2 = new File("/home/hoge.txt"); + public void TestFileObjectStoreInvalidDirectories() { + File fileDir = new File("/invalid_athenz/zms_store"); + File quotaDir = new File("/invalid_athenz/zms_quota"); + FileObjectStore store = null; try { - new FileObjectStore(file); + store = new FileObjectStore(fileDir, quotaDir); + fail(); } catch (Exception ex) { - assertTrue(true); - } - - try { - new FileObjectStore(file2); - } catch (Exception ex) { - assertTrue(true); } + assertNull(store); } } diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/store/jdbc/JDBCConnectionTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/store/jdbc/JDBCConnectionTest.java index 88edb0ad22e..87066e9296b 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/store/jdbc/JDBCConnectionTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/store/jdbc/JDBCConnectionTest.java @@ -26,6 +26,7 @@ import com.yahoo.athenz.zms.Policy; import com.yahoo.athenz.zms.PrincipalRole; import com.yahoo.athenz.zms.PublicKeyEntry; +import com.yahoo.athenz.zms.Quota; import com.yahoo.athenz.zms.ResourceAccess; import com.yahoo.athenz.zms.ResourceAccessList; import com.yahoo.athenz.zms.ResourceException; @@ -5702,4 +5703,278 @@ public void testDeletePrincipalSubDomainException() throws Exception { } jdbcConn.close(); } + + @Test + public void testGetQuota() throws Exception { + + Mockito.when(mockResultSet.next()).thenReturn(true); + Mockito.doReturn(7).when(mockResultSet).getInt(1); // domain id + Mockito.doReturn(10).when(mockResultSet).getInt(ZMSConsts.DB_COLUMN_ASSERTION); + Mockito.doReturn(11).when(mockResultSet).getInt(ZMSConsts.DB_COLUMN_ROLE); + Mockito.doReturn(12).when(mockResultSet).getInt(ZMSConsts.DB_COLUMN_ROLE_MEMBER); + Mockito.doReturn(13).when(mockResultSet).getInt(ZMSConsts.DB_COLUMN_POLICY); + Mockito.doReturn(14).when(mockResultSet).getInt(ZMSConsts.DB_COLUMN_SERVICE); + Mockito.doReturn(15).when(mockResultSet).getInt(ZMSConsts.DB_COLUMN_SERVICE_HOST); + Mockito.doReturn(16).when(mockResultSet).getInt(ZMSConsts.DB_COLUMN_PUBLIC_KEY); + Mockito.doReturn(17).when(mockResultSet).getInt(ZMSConsts.DB_COLUMN_ENTITY); + Mockito.doReturn(18).when(mockResultSet).getInt(ZMSConsts.DB_COLUMN_SUBDOMAIN); + Mockito.doReturn(new java.sql.Timestamp(1454358916)).when(mockResultSet).getTimestamp(ZMSConsts.DB_COLUMN_MODIFIED); + + JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); + Quota quota = jdbcConn.getQuota("athenz"); + assertNotNull(quota); + assertEquals(quota.getAssertion(), 10); + assertEquals(quota.getRole(), 11); + assertEquals(quota.getRoleMember(), 12); + assertEquals(quota.getPolicy(), 13); + assertEquals(quota.getService(), 14); + assertEquals(quota.getServiceHost(), 15); + assertEquals(quota.getPublicKey(), 16); + assertEquals(quota.getEntity(), 17); + assertEquals(quota.getSubdomain(), 18); + Mockito.verify(mockPrepStmt, times(1)).setString(1, "athenz"); + Mockito.verify(mockPrepStmt, times(1)).setInt(1, 7); + jdbcConn.close(); + } + + @Test + public void testGetQuotaNull() throws Exception { + + Mockito.when(mockResultSet.next()).thenReturn(true).thenReturn(false); + Mockito.doReturn(7).when(mockResultSet).getInt(1); // domain id + + JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); + Quota quota = jdbcConn.getQuota("athenz"); + assertNull(quota); + jdbcConn.close(); + } + + @Test + public void testGetQuotaException() throws Exception { + + Mockito.when(mockPrepStmt.executeQuery()).thenReturn(mockResultSet) + .thenThrow(new SQLException("failed operation", "state", 1001)); + Mockito.when(mockResultSet.next()).thenReturn(true); + Mockito.doReturn(7).when(mockResultSet).getInt(1); // domain id + + JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); + try { + jdbcConn.getQuota("athenz"); + fail(); + } catch (Exception ex) { + assertTrue(true); + } + jdbcConn.close(); + } + + @Test + public void testInsertQuota() throws Exception { + + JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); + + Quota quota = new Quota().setName("athenz") + .setAssertion(10).setEntity(11) + .setPolicy(12).setPublicKey(13) + .setRole(14).setRoleMember(15) + .setService(16).setServiceHost(17) + .setSubdomain(18); + + Mockito.doReturn(1).when(mockPrepStmt).executeUpdate(); + Mockito.when(mockResultSet.next()).thenReturn(true); + Mockito.doReturn(5).when(mockResultSet).getInt(1); // return domain id + + boolean requestSuccess = jdbcConn.insertQuota("athenz", quota); + assertTrue(requestSuccess); + + Mockito.verify(mockPrepStmt, times(1)).setString(1, "athenz"); + Mockito.verify(mockPrepStmt, times(1)).setInt(1, 5); + Mockito.verify(mockPrepStmt, times(1)).setInt(2, 14); + Mockito.verify(mockPrepStmt, times(1)).setInt(3, 15); + Mockito.verify(mockPrepStmt, times(1)).setInt(4, 12); + Mockito.verify(mockPrepStmt, times(1)).setInt(5, 10); + Mockito.verify(mockPrepStmt, times(1)).setInt(6, 16); + Mockito.verify(mockPrepStmt, times(1)).setInt(7, 17); + Mockito.verify(mockPrepStmt, times(1)).setInt(8, 13); + Mockito.verify(mockPrepStmt, times(1)).setInt(9, 11); + Mockito.verify(mockPrepStmt, times(1)).setInt(10, 18); + jdbcConn.close(); + } + + @Test + public void testInsertQuotaInvalidDomain() throws Exception { + + JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); + + Quota quota = new Quota().setName("athenz") + .setAssertion(10).setEntity(11) + .setPolicy(12).setPublicKey(13) + .setRole(14).setRoleMember(15) + .setService(16).setServiceHost(17) + .setSubdomain(18); + + Mockito.when(mockResultSet.next()).thenReturn(false); + + try { + jdbcConn.insertQuota("athenz", quota); + fail(); + } catch (ResourceException ex) { + assertEquals(404, ex.getCode()); + } + jdbcConn.close(); + } + + @Test + public void testInsertQuotaException() throws Exception { + + JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); + + Quota quota = new Quota().setName("athenz") + .setAssertion(10).setEntity(11) + .setPolicy(12).setPublicKey(13) + .setRole(14).setRoleMember(15) + .setService(16).setServiceHost(17) + .setSubdomain(18); + + Mockito.when(mockPrepStmt.executeUpdate()).thenThrow(new SQLException("failed operation", "state", 1001)); + Mockito.when(mockResultSet.next()).thenReturn(true); + Mockito.doReturn(5).when(mockResultSet).getInt(1); // return domain id + + try { + jdbcConn.insertQuota("athenz", quota); + fail(); + } catch (Exception ex) { + assertTrue(true); + } + jdbcConn.close(); + } + + @Test + public void testUpdateQuota() throws Exception { + + JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); + + Quota quota = new Quota().setName("athenz") + .setAssertion(10).setEntity(11) + .setPolicy(12).setPublicKey(13) + .setRole(14).setRoleMember(15) + .setService(16).setServiceHost(17) + .setSubdomain(18); + + Mockito.doReturn(1).when(mockPrepStmt).executeUpdate(); + Mockito.when(mockResultSet.next()).thenReturn(true); + Mockito.doReturn(5).when(mockResultSet).getInt(1); // return domain id + + boolean requestSuccess = jdbcConn.updateQuota("athenz", quota); + assertTrue(requestSuccess); + + Mockito.verify(mockPrepStmt, times(1)).setString(1, "athenz"); + Mockito.verify(mockPrepStmt, times(1)).setInt(1, 14); + Mockito.verify(mockPrepStmt, times(1)).setInt(2, 15); + Mockito.verify(mockPrepStmt, times(1)).setInt(3, 12); + Mockito.verify(mockPrepStmt, times(1)).setInt(4, 10); + Mockito.verify(mockPrepStmt, times(1)).setInt(5, 16); + Mockito.verify(mockPrepStmt, times(1)).setInt(6, 17); + Mockito.verify(mockPrepStmt, times(1)).setInt(7, 13); + Mockito.verify(mockPrepStmt, times(1)).setInt(8, 11); + Mockito.verify(mockPrepStmt, times(1)).setInt(9, 18); + Mockito.verify(mockPrepStmt, times(1)).setInt(10, 5); // domain id + jdbcConn.close(); + } + + @Test + public void testUpdateQuotaInvalidDomain() throws Exception { + + JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); + + Quota quota = new Quota().setName("athenz") + .setAssertion(10).setEntity(11) + .setPolicy(12).setPublicKey(13) + .setRole(14).setRoleMember(15) + .setService(16).setServiceHost(17) + .setSubdomain(18); + + Mockito.when(mockResultSet.next()).thenReturn(false); + + try { + jdbcConn.updateQuota("athenz", quota); + fail(); + } catch (ResourceException ex) { + assertEquals(404, ex.getCode()); + } + jdbcConn.close(); + } + + @Test + public void testUpdateQuotaException() throws Exception { + + JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); + + Quota quota = new Quota().setName("athenz") + .setAssertion(10).setEntity(11) + .setPolicy(12).setPublicKey(13) + .setRole(14).setRoleMember(15) + .setService(16).setServiceHost(17) + .setSubdomain(18); + + Mockito.when(mockPrepStmt.executeUpdate()).thenThrow(new SQLException("failed operation", "state", 1001)); + Mockito.when(mockResultSet.next()).thenReturn(true); + Mockito.doReturn(5).when(mockResultSet).getInt(1); // return domain id + + try { + jdbcConn.updateQuota("athenz", quota); + fail(); + } catch (Exception ex) { + assertTrue(true); + } + jdbcConn.close(); + } + + @Test + public void testDeleteQuota() throws Exception { + + JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); + + Mockito.doReturn(1).when(mockPrepStmt).executeUpdate(); + Mockito.when(mockResultSet.next()).thenReturn(true); + Mockito.doReturn(5).when(mockResultSet).getInt(1); // return domain id + + boolean requestSuccess = jdbcConn.deleteQuota("athenz"); + assertTrue(requestSuccess); + + Mockito.verify(mockPrepStmt, times(1)).setInt(1, 5); + Mockito.verify(mockPrepStmt, times(1)).setString(1, "athenz"); + jdbcConn.close(); + } + + @Test + public void testDeleteQuotaInvalidDomain() throws Exception { + + JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); + Mockito.when(mockResultSet.next()).thenReturn(false); + + try { + jdbcConn.deleteQuota("athenz"); + fail(); + } catch (ResourceException ex) { + assertEquals(404, ex.getCode()); + } + jdbcConn.close(); + } + + @Test + public void testDeleteQuotaException() throws Exception { + + JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); + Mockito.when(mockResultSet.next()).thenReturn(true); + Mockito.doReturn(5).when(mockResultSet).getInt(1); // return domain id + + Mockito.when(mockPrepStmt.executeUpdate()).thenThrow(new SQLException("failed operation", "state", 1001)); + try { + jdbcConn.deleteQuota("athenz"); + fail(); + } catch (Exception ex) { + assertTrue(true); + } + jdbcConn.close(); + } } diff --git a/servers/zts/src/main/java/com/yahoo/athenz/instance/provider/ResourceException.java b/servers/zts/src/main/java/com/yahoo/athenz/instance/provider/ResourceException.java index 658ebf4fde7..e74690c51de 100644 --- a/servers/zts/src/main/java/com/yahoo/athenz/instance/provider/ResourceException.java +++ b/servers/zts/src/main/java/com/yahoo/athenz/instance/provider/ResourceException.java @@ -21,10 +21,13 @@ public class ResourceException extends RuntimeException { public final static int GONE = 410; public final static int PRECONDITION_FAILED = 412; public final static int UNSUPPORTED_MEDIA_TYPE = 415; + public final static int PRECONDITION_REQUIRED = 428; + public final static int TOO_MANY_REQUESTS = 429; + public final static int REQUEST_HEADER_FIELDS_TOO_LARGE = 431; public final static int INTERNAL_SERVER_ERROR = 500; public final static int NOT_IMPLEMENTED = 501; - public final static int SERVICE_UNAVAILABLE = 503; + public final static int NETWORK_AUTHENTICATION_REQUIRED = 511; public static String codeToString(int code) { switch (code) { @@ -45,8 +48,13 @@ public static String codeToString(int code) { case GONE: return "Gone"; case PRECONDITION_FAILED: return "Precondition Failed"; case UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type"; + case PRECONDITION_REQUIRED: return "Precondition Required"; + case TOO_MANY_REQUESTS: return "Too Many Requests"; + case REQUEST_HEADER_FIELDS_TOO_LARGE: return "Request Header Fields Too Large"; case INTERNAL_SERVER_ERROR: return "Internal Server Error"; case NOT_IMPLEMENTED: return "Not Implemented"; + case SERVICE_UNAVAILABLE: return "Service Unavailable"; + case NETWORK_AUTHENTICATION_REQUIRED: return "Network Authentication Required"; default: return "" + code; } } diff --git a/servers/zts/src/main/java/com/yahoo/athenz/zts/ResourceException.java b/servers/zts/src/main/java/com/yahoo/athenz/zts/ResourceException.java index e972cc06d22..0cb5fa274b7 100644 --- a/servers/zts/src/main/java/com/yahoo/athenz/zts/ResourceException.java +++ b/servers/zts/src/main/java/com/yahoo/athenz/zts/ResourceException.java @@ -21,10 +21,13 @@ public class ResourceException extends RuntimeException { public final static int GONE = 410; public final static int PRECONDITION_FAILED = 412; public final static int UNSUPPORTED_MEDIA_TYPE = 415; + public final static int PRECONDITION_REQUIRED = 428; + public final static int TOO_MANY_REQUESTS = 429; + public final static int REQUEST_HEADER_FIELDS_TOO_LARGE = 431; public final static int INTERNAL_SERVER_ERROR = 500; public final static int NOT_IMPLEMENTED = 501; - public final static int SERVICE_UNAVAILABLE = 503; + public final static int NETWORK_AUTHENTICATION_REQUIRED = 511; public static String codeToString(int code) { switch (code) { @@ -45,8 +48,13 @@ public static String codeToString(int code) { case GONE: return "Gone"; case PRECONDITION_FAILED: return "Precondition Failed"; case UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type"; + case PRECONDITION_REQUIRED: return "Precondition Required"; + case TOO_MANY_REQUESTS: return "Too Many Requests"; + case REQUEST_HEADER_FIELDS_TOO_LARGE: return "Request Header Fields Too Large"; case INTERNAL_SERVER_ERROR: return "Internal Server Error"; case NOT_IMPLEMENTED: return "Not Implemented"; + case SERVICE_UNAVAILABLE: return "Service Unavailable"; + case NETWORK_AUTHENTICATION_REQUIRED: return "Network Authentication Required"; default: return "" + code; } } From bb39aa158b06830261acbb6f5154b5521a215df5 Mon Sep 17 00:00:00 2001 From: Henry Avetisyan Date: Tue, 4 Jul 2017 23:37:40 -0700 Subject: [PATCH 2/2] Provide option to disable quota check --- servers/zms/conf/zms.properties | 4 ++ .../com/yahoo/athenz/zms/QuotaChecker.java | 56 +++++++++++++++++++ .../java/com/yahoo/athenz/zms/ZMSConsts.java | 1 + 3 files changed, 61 insertions(+) diff --git a/servers/zms/conf/zms.properties b/servers/zms/conf/zms.properties index ccff6bcc1dd..3a8323426b7 100644 --- a/servers/zms/conf/zms.properties +++ b/servers/zms/conf/zms.properties @@ -203,6 +203,10 @@ athenz.zms.solution_templates_fname=${ROOT}/conf/zms_server/solution_templates.j # ZMS are only accepted on secure TLS ports. #athenz.zms.secure_requests_only=true +# Quota Support: boolean value defining whether or not quota +# check is enabled or not. +#athenz.zms.quota_check=true + # Quota Support: default number of roles allowed to be created # in a given domain. #athenz.zms.quota_role=1000 diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/QuotaChecker.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/QuotaChecker.java index d85c2ef6eb4..23c2513c91e 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/QuotaChecker.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/QuotaChecker.java @@ -9,8 +9,16 @@ public class QuotaChecker { Quota defaultQuota; + boolean quotaCheckEnabled = true; public QuotaChecker() { + + // first check if the quota check is enabled or not + + quotaCheckEnabled = Boolean.parseBoolean(System.getProperty(ZMSConsts.ZMS_PROP_QUOTA_CHECK, "true")); + + // retrieve default quota values + int roleQuota = Integer.parseInt(System.getProperty(ZMSConsts.ZMS_PROP_QUOTA_ROLE, "1000")); int roleMemberQuota = Integer.parseInt(System.getProperty(ZMSConsts.ZMS_PROP_QUOTA_ROLE_MEMBER, "100")); int policyQuota = Integer.parseInt(System.getProperty(ZMSConsts.ZMS_PROP_QUOTA_POLICY, "1000")); @@ -40,6 +48,12 @@ int getListSize(List list) { void checkSubdomainQuota(ObjectStoreConnection con, String domainName, String caller) { + // if quota check is disabled we have nothing to do + + if (!quotaCheckEnabled) { + return; + } + // for sub-domains we need to run the quota check against // the top level domain so let's get that first. If we are // creating a top level domain then there is no need for @@ -69,6 +83,12 @@ void checkSubdomainQuota(ObjectStoreConnection con, String domainName, String ca void checkRoleQuota(ObjectStoreConnection con, String domainName, Role role, String caller) { + // if quota check is disabled we have nothing to do + + if (!quotaCheckEnabled) { + return; + } + // if our role is null then there is no quota check if (role == null) { @@ -101,6 +121,12 @@ void checkRoleQuota(ObjectStoreConnection con, String domainName, Role role, Str void checkRoleMembershipQuota(ObjectStoreConnection con, String domainName, List roleMembers, String caller) { + // if quota check is disabled we have nothing to do + + if (!quotaCheckEnabled) { + return; + } + // first retrieve the domain quota final Quota quota = getDomainQuota(con, domainName); @@ -117,6 +143,12 @@ void checkRoleMembershipQuota(ObjectStoreConnection con, String domainName, void checkPolicyQuota(ObjectStoreConnection con, String domainName, Policy policy, String caller) { + // if quota check is disabled we have nothing to do + + if (!quotaCheckEnabled) { + return; + } + // if our policy is null then there is no quota check if (policy == null) { @@ -149,6 +181,12 @@ void checkPolicyQuota(ObjectStoreConnection con, String domainName, Policy polic void checkPolicyAssertionQuota(ObjectStoreConnection con, String domainName, List assertions, String caller) { + // if quota check is disabled we have nothing to do + + if (!quotaCheckEnabled) { + return; + } + // first retrieve the domain quota final Quota quota = getDomainQuota(con, domainName); @@ -166,6 +204,12 @@ void checkPolicyAssertionQuota(ObjectStoreConnection con, String domainName, void checkServiceIdentityQuota(ObjectStoreConnection con, String domainName, ServiceIdentity service, String caller) { + // if quota check is disabled we have nothing to do + + if (!quotaCheckEnabled) { + return; + } + // if our service is null then there is no quota check if (service == null) { @@ -204,6 +248,12 @@ void checkServiceIdentityQuota(ObjectStoreConnection con, String domainName, void checkServiceIdentityPublicKeyQuota(ObjectStoreConnection con, String domainName, List publicKeys, String caller) { + // if quota check is disabled we have nothing to do + + if (!quotaCheckEnabled) { + return; + } + // first retrieve the domain quota final Quota quota = getDomainQuota(con, domainName); @@ -221,6 +271,12 @@ void checkServiceIdentityPublicKeyQuota(ObjectStoreConnection con, String domain void checkEntityQuota(ObjectStoreConnection con, String domainName, Entity entity, String caller) { + // if quota check is disabled we have nothing to do + + if (!quotaCheckEnabled) { + return; + } + // if our entity is null then there is no quota check if (entity == null) { 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 0109b25d8fa..4cb098665d1 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 @@ -64,6 +64,7 @@ public final class ZMSConsts { // properties for our default quota limits + public static final String ZMS_PROP_QUOTA_CHECK = "athenz.zms.quota_check"; public static final String ZMS_PROP_QUOTA_ROLE = "athenz.zms.quota_role"; public static final String ZMS_PROP_QUOTA_ROLE_MEMBER = "athenz.zms.quota_role_member"; public static final String ZMS_PROP_QUOTA_POLICY = "athenz.zms.quota_policy";