Repository navigation
Policy Conditions
Condition blocks in VersityGW use AWS's policy-condition language unchanged — the block's JSON shape, the AND/AND/OR combining rules, the operator semantics, and the IfExists / ForAllValues: / ForAnyValue: modifiers all behave as AWS's IAM condition-element reference describes them. This page covers only what differs: which condition keys actually exist, where the parser is stricter or looser than AWS's, and what an absent key does.
Bucket policies, identity policies attached to an IAM user or role, and role trust policies all go through one shared condition implementation — the operator registry, IfExists, the set qualifiers, multi-value aggregation, and the ${...} rules are identical across the three. Only the available key set differs, and a key that is never populated for the policy type you are writing is simply absent at evaluation time. An absent key is by far the most common reason a policy copied from AWS documentation appears to do nothing, or the opposite of what it says — see When a key is absent.
| Bucket policy | Identity policy | Role trust policy | |
|---|---|---|---|
| Operator names validated at write time | yes | yes | yes |
| Condition keys validated at write time | yes — a fixed catalogue of 17 keys | no — any key name is accepted | no — any key name is accepted |
| Condition values validated at write time | only aws:SourceIp, which must be an IP or CIDR |
no | no |
Default Version when the element is omitted |
2008-10-17 |
empty (neither version) | empty (neither version) |
The conditions array inside a browser POST-Object form policy is a completely separate mechanism with its own grammar. Nothing on this page applies to it.
The operator set is complete: all 26 AWS comparison operators plus Null, each optionally carrying the IfExists suffix and one of the ForAllValues: / ForAnyValue: qualifiers.
-
String —
StringEquals,StringNotEquals,StringEqualsIgnoreCase,StringNotEqualsIgnoreCase,StringLike,StringNotLike -
Numeric —
NumericEquals,NumericNotEquals,NumericLessThan,NumericLessThanEquals,NumericGreaterThan,NumericGreaterThanEquals -
Date —
DateEquals,DateNotEquals,DateLessThan,DateLessThanEquals,DateGreaterThan,DateGreaterThanEquals -
Bool —
Bool -
Binary —
BinaryEquals -
Arn —
ArnEquals,ArnLike,ArnNotEquals,ArnNotLike -
IP —
IpAddress,NotIpAddress -
Presence —
Null
Per-family behavior worth knowing before you write one:
| Family | Behavior |
|---|---|
| Numeric | Both sides are parsed as float64. An unparseable value on either side makes the comparison false — never an error, never aborting evaluation. |
| Date | Accepts RFC 3339 (with or without fractional seconds) and Unix epoch seconds (optionally fractional), and the two forms are interchangeable across a comparison. A bare calendar date (2026-06-15), ISO 8601 without the T separator or the zone offset, and RFC 1123 all fail to parse — such a policy silently never matches. |
| Bool | Go's ParseBool, so "t", "1", "TRUE", "True" all parse, as does a bare JSON true / false. An unparseable value on either side is false. There is no BoolNotEquals — write {"Bool": {"aws:SecureTransport": "false"}}. |
| Binary |
BinaryEquals is a literal string comparison of the base64 text, not decode-then-compare. |
| Arn | The same flat whole-string glob StringLike uses. There is no ARN-component-aware parsing — the ARN is never split into partition, service, region, account and resource — so ArnEquals ≡ ArnLike and ArnNotEquals ≡ ArnNotLike. |
| IP | An unparseable range, or an unparseable request address, never matches. In a bucket policy aws:SourceIp is additionally validated at write time — see Keys available in bucket policies. |
| Null | Any value other than true / false never holds — the condition fails closed. NullIfExists is rejected at write time. ForAllValues:Null and ForAnyValue:Null are accepted, with the qualifier a silent no-op; that is more permissive than AWS, so write bare Null and do not rely on it. |
A few grammar points the parser enforces:
- Condition key names are looked up case-insensitively —
aws:SourceIp,AWS:SOURCEIP, andaws:sourceipare one key. Condition values stay case-sensitive, except under theIgnoreCaseoperators. - A value may be a JSON string, a bare number, or a bare boolean. Numbers keep their literal text, so
5.50stays"5.50"and is not normalized to5.5. - An absent,
null, or emptyConditionblock always matches. So does an operator with an empty key map, such as{"StringEquals": {}}. - An unqualified negated operator requires every request value to fail against every policy value.
Rejected at the grammar level:
| Written | Why it is rejected |
|---|---|
NullIfExists |
Null has no IfExists variant (neither does AWS) |
stringequals, STRINGEQUALS, StringEQUALS
|
operator names are case-sensitive |
ForSomeValues:StringEquals, ForAllValue:StringEquals
|
only ForAllValues: and ForAnyValue: are qualifiers |
BinaryNotEquals, NumericLike, DateLike, BoolNotEquals
|
no such operators (neither in AWS) |
{"StringEquals": "foo"}, {"StringEquals": ["a"]}
|
an operator's value must be an object of key/value pairs |
{"StringEquals": {"k": null}}, {"StringEquals": {"k": [["a"]]}}, {"StringEquals": {"k": {}}}
|
a condition value must be a string, number, or boolean scalar, or an array of those |
The operator set is complete. What is missing is condition keys, plus four gateway-wide gaps:
-
No policy-variable substitution outside the String and Arn families. A
${...}placeholder inside aNumeric*,Date*,Bool,BinaryEquals,IpAddress,NotIpAddress, orNullvalue is left as literal text and simply fails to match. -
No condition-key validation for identity or trust policies. Any key name is accepted at write time, so a typo such as
aws:SourceIPAddressis stored and evaluates as an absent key forever after. (aws:SourceIPdoes resolve, because key lookup is case-insensitive.) - No value validation for identity or trust policies. A malformed CIDR, an unparseable date, or a non-numeric numeric value is accepted at write time and quietly never matches.
-
No editing support in the WebGUI. Policy documents,
Conditionblocks included, are edited as raw JSON.
An unrecognized operator name is rejected at write time, not ignored at evaluation time. Which error you get depends on the policy type:
| API | Error code | HTTP | Message |
|---|---|---|---|
PutBucketPolicy |
MalformedPolicy |
400 |
Invalid Condition type : <Name> — the name is echoed exactly as written |
PutUserPolicy, PutRolePolicy, and the Policy session-policy parameter |
MalformedPolicyDocument |
400 | Syntax errors in policy. |
CreateRole, UpdateAssumeRolePolicy
|
MalformedPolicyDocument |
400 |
Syntax error in policy. (singular, unlike the identity-policy message) |
Identity and trust policies reject two further things inside a Condition block that a bucket policy accepts: a duplicate JSON key at any nesting depth — two of the same operator, or the same condition key twice inside one operator — is rejected outright rather than silently keeping the last occurrence, and unknown fields on a statement object are rejected.
Important
If a document containing an unrecognized operator does reach storage anyway — written before this validation existed, restored from a backup, or written out of band — the Condition cannot be evaluated and every evaluator fails closed, on the reasoning that an unevaluable condition might have been hiding a Deny. A bucket policy denies the request. An identity policy denies the entire evaluation, including every other document attached to the principal. A trust policy produces AccessDenied from AssumeRoleWithWebIdentity. Run with --log-level debug (Debugging) to see policy condition block failed to parse in the log.
VersityGW populates none of the keys below. A bucket policy rejects them at write time with MalformedPolicy / Policy has an invalid condition key; an identity or trust policy accepts them and then evaluates them as absent forever — exactly the fail-open shape described in When a key is absent.
- Tags on S3 objects —
s3:ExistingObjectTag/<key>,s3:RequestObjectTag/<key>,s3:RequestObjectTagKeys, andaws:ResourceTag/<key>naming an S3 bucket or object tag. - Object Lock — every
s3:object-lock-*key. - Encryption, storage class, copy —
s3:x-amz-server-side-encryption,s3:x-amz-server-side-encryption-aws-kms-key-id,s3:x-amz-storage-class,s3:x-amz-copy-source,s3:x-amz-metadata-directive. - Request shape —
s3:signatureversion,s3:authType,s3:TlsVersion,s3:DataAccessPointArn,s3:AccessPointNetworkOrigin. - MFA, VPC, cross-account, organization, region, federation —
aws:MultiFactorAuthPresent,aws:SourceVpc,aws:SourceVpce,aws:VpcSourceIp,aws:SourceArn,aws:SourceAccount,aws:SourceOwner,aws:PrincipalOrgID,aws:PrincipalOrgPaths,aws:PrincipalIsAWSService,aws:RequestedRegion,aws:ViaAWSService,aws:CalledVia,aws:CalledViaFirst,aws:CalledViaLast,aws:FederatedProvider,aws:TokenIssueTime.
One exception to the write-time rule: aws:MultiFactorAuthAge is accepted by PutBucketPolicy, though never populated, while aws:MultiFactorAuthPresent is rejected.
There is no MFA concept in the gateway, no VPC concept, no region concept, and no cross-account concept — this deployment is a single account, 000000000000. S3 bucket and object tags are not exposed as condition keys anywhere.
A bucket policy's Condition is evaluated entirely on the S3 gateway, against a context built from the HTTP request. PutBucketPolicy accepts exactly 17 key names — anything else is rejected with MalformedPolicy / Policy has an invalid condition key — and of those 17, 13 are ever populated.
| Key | Operator family | Source | Present when |
|---|---|---|---|
aws:CurrentTime |
Date | server time, RFC 3339 with second precision | every request |
aws:EpochTime |
Numeric or Date | server time, integer epoch seconds | every request |
aws:SecureTransport |
Bool | whether the S3 connection is TLS | every request |
aws:SourceIp |
IpAddress | the connection's peer address | every request (see the proxy caveat below) |
aws:UserAgent |
String | the User-Agent header |
when the header is non-empty |
aws:Referer |
String | the Referer header |
when the header is non-empty |
s3:prefix |
String | the prefix query parameter |
when ?prefix= is non-empty |
s3:delimiter |
String | the delimiter query parameter |
when ?delimiter= is non-empty |
s3:max-keys |
Numeric or String | the max-keys query parameter, unparsed
|
when ?max-keys= is non-empty |
s3:x-amz-acl |
String | the X-Amz-Acl header |
when the header is non-empty |
s3:VersionId |
String | the versionId query parameter |
when ?versionId= is non-empty |
s3:if-match |
String | the If-Match header, with one pair of surrounding ETag quotes removed |
conditional PutObject, CompleteMultipartUpload, or unversioned DeleteObject with a non-empty header |
s3:if-none-match |
String | the If-None-Match header, with one pair of surrounding ETag quotes removed |
conditional PutObject or CompleteMultipartUpload with a non-empty header |
aws:PrincipalArn, aws:username, aws:userid, aws:MultiFactorAuthAge
|
— | — | never — see the warning below |
The same request context is used for anonymous and public requests, so a public statement's Condition sees the same request-derived facts an authenticated one would.
The seven s3: keys carry a write-time action restriction: a statement naming one of them must name only actions the key applies to.
| Key | Allowed actions |
|---|---|
s3:prefix, s3:delimiter, s3:max-keys
|
s3:ListBucket, s3:ListBucketVersions
|
s3:x-amz-acl |
s3:PutObject, s3:PutBucketAcl, s3:PutObjectAcl
|
s3:VersionId |
s3:GetObjectVersion, s3:DeleteObjectVersion, s3:GetObjectVersionAttributes, s3:GetObjectVersionTagging, s3:PutObjectVersionTagging, s3:DeleteObjectVersionTagging
|
s3:if-match |
s3:PutObject, s3:DeleteObject
|
s3:if-none-match |
s3:PutObject |
Every concrete action named in the statement must be in the list — an explicit ["s3:GetObject", "s3:PutObject"] with s3:x-amz-acl is rejected even though s3:PutObject alone would be accepted — and a violation returns MalformedPolicy / Conditions do not apply to combination of actions and resources in statement. Note that s3:ListBucketMultipartUploads is deliberately not in the list-action set. An action pattern containing * or ? is exempt from the check entirely, so "Action": "s3:*" accepts any key from the catalogue.
aws:SourceIp is additionally validated as an IP address or CIDR range at write time, no matter which operator wraps it: {"Null": {"aws:SourceIp": "true"}} is rejected with Invalid IP address in Conditions for that reason. The check is keyed to the key, not to the operator.
Two consequences of the context being built from the request alone:
-
It is not action-gated.
s3:prefix,s3:delimiter,s3:max-keys,s3:x-amz-acl, ands3:VersionIdare populated from the query string and headers for every operation, not only the ones the write-time catalogue permits. A statement with"Action": "s3:*"may legally carrys3:prefix, and that condition is then also evaluated against, say, aGetObjectwhose URL happens to carry?prefix=. -
The conditional-write keys are action-gated. Unlike the five keys above,
s3:if-matchands3:if-none-matchare populated only when the header controls an object write. They remain absent for GET/HEAD cache conditions,CopyObjectsource preconditions,UploadPart,UploadPartCopy,DeleteObjects, versioned deletes, and object subresource operations.CompleteMultipartUploadis included because it is authorized ass3:PutObject. -
aws:SourceIpis the real peer address.X-Forwarded-Forand other proxy headers are deliberately not trusted — honouring one would makeaws:SourceIpclient-controlled. Behind a reverse proxy or load balanceraws:SourceIpis therefore the proxy's address, and an IP-based policy will not distinguish clients; enforce source-IP restrictions at the proxy in that topology.
An identity policy is an inline policy on an IAM user or role, evaluated by the standalone IAM service. Two request paths reach it, carrying different context: an S3 request, where the gateway sends the request-derived keys it observed over the private endpoint and the IAM service overlays the identity-derived keys it alone can know; and an IAM API request, where the IAM service builds the whole context itself.
| Key | Operator family | Source | S3 request | IAM API request |
|---|---|---|---|---|
aws:CurrentTime |
Date | request time, RFC 3339 | yes | yes |
aws:EpochTime |
Numeric or Date | request time, epoch seconds | yes | yes |
aws:SecureTransport |
Bool | TLS on the connection | yes | yes |
aws:SourceIp |
IpAddress | peer address | yes | yes |
aws:UserAgent |
String |
User-Agent header |
yes | no |
aws:Referer |
String |
Referer header |
yes | no |
s3:prefix, s3:delimiter, s3:max-keys, s3:x-amz-acl, s3:VersionId, s3:if-match, s3:if-none-match
|
String / Numeric | the S3 request, as in the bucket-policy table above | yes | no |
aws:PrincipalArn |
String or Arn | the user's Arn, or the assumed role's ARN (arn:aws:iam::000000000000:role/<RoleName>) for a session |
yes | yes |
aws:PrincipalAccount |
String | always 000000000000
|
yes | yes |
aws:PrincipalType |
String |
User or AssumedRole
|
yes | yes |
aws:username |
String | the IAM user's name. Absent for an assumed-role session | users only | users only |
aws:userid |
String | the user's id, or <RoleId>:<RoleSessionName> for a session |
yes | yes |
aws:PrincipalTag/<key> |
String | the calling user's own IAM tags, or the assumed role's tags for a session | yes | yes |
iam:ResourceTag/<key> and aws:ResourceTag/<key>
|
String | the tags on the target IAM entity of the call, under both key forms | no | yes |
aws:RequestTag/<key> |
String | the Tags parameter of the call |
no |
CreateUser, CreateRole, TagUser, TagRole, CreateOpenIDConnectProvider, TagOpenIDConnectProvider
|
aws:TagKeys |
String, multi-valued | the tag keys the call supplies | no | the actions above, plus UntagUser, UntagRole, UntagOpenIDConnectProvider
|
Notes that matter in practice:
-
aws:PrincipalArnfor a session is the role's ARN, not the session's. AConditionon it therefore applies to every session of that role and can never single one out;aws:userid, which carries<RoleId>:<RoleSessionName>, is the key that can. Denial messages are different — they still name the session by itsarn:aws:sts::000000000000:assumed-role/<role>/<session>ARN. -
aws:usernameis absent for a session. Anything conditioned on it silently changes meaning once the caller assumes a role instead of using long-term keys. Useaws:PrincipalArnoraws:useridwhen you need to cover both. -
aws:PrincipalTag/<key>for a session comes from the role's tags. There is no session-tag parameter onAssumeRoleWithWebIdentity, so the role's tags are the session's tags for the session's whole lifetime. -
aws:TagKeysis the only multi-valued key on this path, and the only one where the set qualifiers do anything useful. A request may carry at most 50 tags, so at most 50aws:RequestTag/<key>entries can appear. -
Tag-key casing does not reach a
Condition. The context is keyed with the tag key exactly as supplied, but condition-key names are looked up case-insensitively over the whole name,/<key>suffix included. Soaws:RequestTag/envandaws:RequestTag/ENVare the same condition key even on an OIDC provider, whereenvandENVare two independent tags: aConditioncannot single out one casing, and when one request supplies both, the value the condition sees is whichever key the lookup finds first.aws:TagKeysvalues are unaffected — they are compared by the operator, soStringEqualson them stays case-sensitive. -
aws:ResourceTag/<key>never describes an S3 bucket or object. On the S3 path it is stripped from the gateway's contribution and never re-added. - The IAM service filters every identity- and resource-namespaced key out of the gateway's contribution before overlaying its own, rather than merging with override-on-collision. A gateway cannot smuggle in
aws:PrincipalTag/xfor an untagged role, which is precisely the value aStringNotEquals-guardedAllowkeys off. - The root account bypasses policy evaluation entirely — no condition, and no identity policy, applies to it. See Identity Policies.
A role's AssumeRolePolicyDocument is evaluated during AssumeRoleWithWebIdentity, against the claims of the presented OIDC token. There is no caller identity yet, so no aws:PrincipalArn, aws:username, or aws:userid exists here. <provider> below is the token's iss claim with a leading https:// stripped — for example token.actions.githubusercontent.com.
| Key | Source |
|---|---|
<provider>:sub |
the sub claim |
<provider>:aud |
the effective audience: the azp claim when present, otherwise the single aud value |
<provider>:oaud |
the original aud value(s) — set only when azp is present |
<provider>:<claim> |
every other top-level scalar or scalar-array claim |
aws:SourceIp |
peer address, when non-empty |
aws:SecureTransport |
whether the connection is TLS — always present |
aws:CurrentTime, aws:EpochTime
|
request time |
sts:RoleSessionName |
the RoleSessionName request parameter, when non-empty. This key exists only in trust policies |
iss, aud, azp, and sub are excluded from the generic <provider>:<claim> projection — the first is the provider itself and the rest have dedicated keys — as are exp, iat, and nbf, which are timestamps rather than condition material.
Claim values are coerced to strings, which is what makes Bool, Numeric*, and Date* conditions against custom claims work: a JSON string is taken as-is, a number is formatted without a trailing .0, a boolean becomes "true" / "false", a nested object or null is dropped, a JSON array yields a multi-valued key, and an array with no usable scalars produces no key at all.
A trust statement whose Condition fails yields InvalidIdentityToken, not AccessDenied; an explicit Deny, or a condition that cannot be evaluated, yields AccessDenied. See STS & Web Identity for the full flow, the error matrix, and the extra rule that a trust policy naming a shared-audience OIDC provider must scope that provider's tenancy claim with StringEquals, StringLike, or StringEqualsIgnoreCase.
Warning
aws:PrincipalArn, aws:username, aws:userid, and aws:PrincipalTag/<key> never match in a bucket policy. The S3 gateway evaluates bucket policies on its own and has no way to know who an access key belongs to — resolving that is the standalone IAM service's job, and it only does so while evaluating an identity policy.
- The first three are nonetheless accepted by
PutBucketPolicy;aws:PrincipalTag/<key>is rejected as an invalid condition key. - A positive operator on any of them therefore never matches, and a negated one always matches for everybody: on an
Allowthat grants every principal the statement names, on aDenyit denies every one of them. - To scope a bucket policy to particular callers, use the statement's
Principalelement. To scope by an identity attribute such as a tag, use an identity policy attached to the user or role.
A condition key is "absent" whenever the request context does not carry it — a key VersityGW never populates, a key populated only under certain circumstances (an empty User-Agent, a ListObjectsV2 with no prefix), or a plain typo in the key name.
| Operator shape | Key absent means |
|---|---|
Null with "true"
|
condition holds |
Null with "false"
|
condition fails |
Null with any other value |
condition fails |
positive operator, no qualifier, no IfExists — StringEquals, IpAddress, Bool, … |
condition fails, so the statement does not apply |
positive operator with IfExists
|
condition holds |
any negated operator — StringNotEquals, ArnNotLike, NotIpAddress, … — with or without IfExists
|
condition holds (vacuously true) |
ForAllValues: with anything |
condition holds (vacuously true) |
ForAnyValue: with a positive operator |
condition fails, unless IfExists is also present |
ForAnyValue: with a negated operator |
condition holds |
A key that is present but carries an empty value list behaves like an absent key for the set qualifiers — ForAllValues: (and an unqualified negated operator) is vacuously true, ForAnyValue: (and an unqualified positive operator) is vacuously false — but Null still sees it as present.
Important
This is the most important safety property on this page. A Deny guarded by a negated operator on a key that is never populated fires for everyone; an Allow guarded the same way grants everyone. Before writing NotIpAddress, StringNotEquals, or ArnNotEquals, confirm from the tables above that the key is actually populated for the policy type you are writing. The safe pattern is to pair the guard with a presence test on the same key, since the two blocks are ANDed:
"Condition": {
"Null": { "aws:SourceIp": "false" },
"NotIpAddress": { "aws:SourceIp": "203.0.113.0/24" }
}A ${...} placeholder inside a condition value is replaced with the request context's value for the named key. Substitution happens only when the enclosing document's Version is exactly "2012-10-17" and the operator's base name is in the String or Arn family; everywhere else the ${...} stays literal text.
- A placeholder name may contain letters, digits,
_,:,., and-. It resolves with the same case-insensitive lookup used for condition keys, so${aws:username}and${AWS:UserName}are the same variable. - A placeholder that cannot be resolved — the named key is absent, or holds more than one value — is left literal, never expanded to an empty string. That is the safe fallback: the condition simply fails to match rather than silently collapsing a
Denyinto a no-op. - A bucket policy that omits
Versiondefaults to2008-10-17, which disables substitution — write"Version": "2012-10-17"explicitly.2012-10-17and2008-10-17are the only accepted values; anything else is rejected withThe policy must contain a valid version string. An identity or trust policy may omitVersionentirely, which likewise disables substitution. - The same substitution applies to an identity policy's
ResourceandNotResourcepatterns, under the same version rule. It does not apply to a bucket policy'sResourcepatterns, which are always matched literally.
A bucket policy allowing one principal to list media, but only under the photos/ prefix.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListPhotosPrefixOnly",
"Effect": "Allow",
"Principal": "arn:aws:iam::000000000000:user/alice",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::media",
"Condition": {
"StringEquals": { "s3:prefix": "photos/" }
}
}
]
}The Principal form depends on the IAM backend — a principal ARN under the standalone IAM service, the account's access key ID under the built-in backends; see Bucket Policies for the accepted forms. PutBucketPolicy rejects a principal that does not resolve, with MalformedPolicy / Invalid principal in policy.
The following bucket policy follows the AWS conditional-write enforcement
pattern. The explicit Deny rejects any upload that does not carry
If-None-Match; the unconditional Allow supplies the underlying
permission:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowObjectUploads",
"Effect": "Allow",
"Principal": "arn:aws:iam::000000000000:user/alice",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::media/*"
},
{
"Sid": "RequireIfNoneMatch",
"Effect": "Deny",
"Principal": "arn:aws:iam::000000000000:user/alice",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::media/*",
"Condition": {
"Null": { "s3:if-none-match": "true" }
}
}
]
}Clients must send:
If-None-Match: *The * is preserved as the condition value. With StringEquals, it is a
literal asterisk, not a wildcard. A request without the header is denied by
the policy. A request with the header is authorized, after which the storage
backend evaluates the precondition and returns 412 PreconditionFailed if
the object already exists.
This statement grants an overwrite only when the request names the expected ETag:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReplaceKnownObjectVersion",
"Effect": "Allow",
"Principal": "arn:aws:iam::000000000000:user/alice",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::media/catalog.json",
"Condition": {
"StringEquals": {
"s3:if-match": "686897696a7c876b7e"
}
}
}
]
}Although an HTTP ETag is normally quoted, write the policy value without
those quotes. Both If-Match: "686897696a7c876b7e" and
If-Match: 686897696a7c876b7e produce the bare condition value
686897696a7c876b7e. A missing or different header fails authorization. A
matching header passes policy evaluation, but the backend still compares it
with the current object's ETag and returns 412 PreconditionFailed if the
condition is stale.
Use the same key with s3:DeleteObject to authorize conditional,
unversioned deletes. It does not apply to s3:DeleteObjectVersion. To allow
any conditional write without pinning a particular ETag, use:
"Condition": {
"Null": { "s3:if-match": "false" }
}These keys control authorization; they do not make the underlying update atomic by themselves. The selected storage backend must also support conditional object publication. See the conditional publish lock modes under POSIX Backend and ScoutFS Backend.
Home · Quickstart · Configuration · Operations · Testing · Developer Guide · GitHub · Discussions · Issues
Apache 2.0 · @versitysoftware · LinkedIn · X · Facebook · Instagram
- Home
- Key Features
- User Guide
- Getting Started
- Networking and Deployment
- Access Control / IAM
- Features
- Backends
- Compatibility
- Operations
- Metrics
- Admin APIs
- Logging
- S3 RDMA
- Developer Guide
- Articles