packages feed

amazonka-iam-policy (empty) → 0.0.1

raw patch · 19 files changed

+2531/−0 lines, 19 filesdep +aesondep +aeson-prettydep +amazonka-iam-policy

Dependencies added: aeson, aeson-pretty, amazonka-iam-policy, base, base64-bytestring, bytestring, doctest, hspec, profunctors, scientific, text, time

Files

+ CHANGELOG.md view
+ LICENSE view
@@ -0,0 +1,367 @@+Mozilla Public License Version 2.0+==================================++1. Definitions+--------------++1.1. "Contributor"+    means each individual or legal entity that creates, contributes to+    the creation of, or owns Covered Software.++1.2. "Contributor Version"+    means the combination of the Contributions of others (if any) used+    by a Contributor and that particular Contributor's Contribution.++1.3. "Contribution"+    means Covered Software of a particular Contributor.++1.4. "Covered Software"+    means Source Code Form to which the initial Contributor has attached+    the notice in Exhibit A, the Executable Form of such Source Code+    Form, and Modifications of such Source Code Form, in each case+    including portions thereof.++1.5. "Incompatible With Secondary Licenses"+    means++    (a) that the initial Contributor has attached the notice described+        in Exhibit B to the Covered Software; or++    (b) that the Covered Software was made available under the terms of+        version 1.1 or earlier of the License, but not also under the+        terms of a Secondary License.++1.6. "Executable Form"+    means any form of the work other than Source Code Form.++1.7. "Larger Work"+    means a work that combines Covered Software with other material, in+    a separate file or files, that is not Covered Software.++1.8. "License"+    means this document.++1.9. "Licensable"+    means having the right to grant, to the maximum extent possible,+    whether at the time of the initial grant or subsequently, any and+    all of the rights conveyed by this License.++1.10. "Modifications"+    means any of the following:++    (a) any file in Source Code Form that results from an addition to,+        deletion from, or modification of the contents of Covered+        Software; or++    (b) any new file in Source Code Form that contains any Covered+        Software.++1.11. "Patent Claims" of a Contributor+    means any patent claim(s), including without limitation, method,+    process, and apparatus claims, in any patent Licensable by such+    Contributor that would be infringed, but for the grant of the+    License, by the making, using, selling, offering for sale, having+    made, import, or transfer of either its Contributions or its+    Contributor Version.++1.12. "Secondary License"+    means either the GNU General Public License, Version 2.0, the GNU+    Lesser General Public License, Version 2.1, the GNU Affero General+    Public License, Version 3.0, or any later versions of those+    licenses.++1.13. "Source Code Form"+    means the form of the work preferred for making modifications.++1.14. "You" (or "Your")+    means an individual or a legal entity exercising rights under this+    License. For legal entities, "You" includes any entity that+    controls, is controlled by, or is under common control with You. For+    purposes of this definition, "control" means (a) the power, direct+    or indirect, to cause the direction or management of such entity,+    whether by contract or otherwise, or (b) ownership of more than+    fifty percent (50%) of the outstanding shares or beneficial+    ownership of such entity.++2. License Grants and Conditions+--------------------------------++2.1. Grants++Each Contributor hereby grants You a world-wide, royalty-free,+non-exclusive license:++(a) under intellectual property rights (other than patent or trademark)+    Licensable by such Contributor to use, reproduce, make available,+    modify, display, perform, distribute, and otherwise exploit its+    Contributions, either on an unmodified basis, with Modifications, or+    as part of a Larger Work; and++(b) under Patent Claims of such Contributor to make, use, sell, offer+    for sale, have made, import, and otherwise transfer either its+    Contributions or its Contributor Version.++2.2. Effective Date++The licenses granted in Section 2.1 with respect to any Contribution+become effective for each Contribution on the date the Contributor first+distributes such Contribution.++2.3. Limitations on Grant Scope++The licenses granted in this Section 2 are the only rights granted under+this License. No additional rights or licenses will be implied from the+distribution or licensing of Covered Software under this License.+Notwithstanding Section 2.1(b) above, no patent license is granted by a+Contributor:++(a) for any code that a Contributor has removed from Covered Software;+    or++(b) for infringements caused by: (i) Your and any other third party's+    modifications of Covered Software, or (ii) the combination of its+    Contributions with other software (except as part of its Contributor+    Version); or++(c) under Patent Claims infringed by Covered Software in the absence of+    its Contributions.++This License does not grant any rights in the trademarks, service marks,+or logos of any Contributor (except as may be necessary to comply with+the notice requirements in Section 3.4).++2.4. Subsequent Licenses++No Contributor makes additional grants as a result of Your choice to+distribute the Covered Software under a subsequent version of this+License (see Section 10.2) or under the terms of a Secondary License (if+permitted under the terms of Section 3.3).++2.5. Representation++Each Contributor represents that the Contributor believes its+Contributions are its original creation(s) or it has sufficient rights+to grant the rights to its Contributions conveyed by this License.++2.6. Fair Use++This License is not intended to limit any rights You have under+applicable copyright doctrines of fair use, fair dealing, or other+equivalents.++2.7. Conditions++Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted+in Section 2.1.++3. Responsibilities+-------------------++3.1. Distribution of Source Form++All distribution of Covered Software in Source Code Form, including any+Modifications that You create or to which You contribute, must be under+the terms of this License. You must inform recipients that the Source+Code Form of the Covered Software is governed by the terms of this+License, and how they can obtain a copy of this License. You may not+attempt to alter or restrict the recipients' rights in the Source Code+Form.++3.2. Distribution of Executable Form++If You distribute Covered Software in Executable Form then:++(a) such Covered Software must also be made available in Source Code+    Form, as described in Section 3.1, and You must inform recipients of+    the Executable Form how they can obtain a copy of such Source Code+    Form by reasonable means in a timely manner, at a charge no more+    than the cost of distribution to the recipient; and++(b) You may distribute such Executable Form under the terms of this+    License, or sublicense it under different terms, provided that the+    license for the Executable Form does not attempt to limit or alter+    the recipients' rights in the Source Code Form under this License.++3.3. Distribution of a Larger Work++You may create and distribute a Larger Work under terms of Your choice,+provided that You also comply with the requirements of this License for+the Covered Software. If the Larger Work is a combination of Covered+Software with a work governed by one or more Secondary Licenses, and the+Covered Software is not Incompatible With Secondary Licenses, this+License permits You to additionally distribute such Covered Software+under the terms of such Secondary License(s), so that the recipient of+the Larger Work may, at their option, further distribute the Covered+Software under the terms of either this License or such Secondary+License(s).++3.4. Notices++You may not remove or alter the substance of any license notices+(including copyright notices, patent notices, disclaimers of warranty,+or limitations of liability) contained within the Source Code Form of+the Covered Software, except that You may alter any license notices to+the extent required to remedy known factual inaccuracies.++3.5. Application of Additional Terms++You may choose to offer, and to charge a fee for, warranty, support,+indemnity or liability obligations to one or more recipients of Covered+Software. However, You may do so only on Your own behalf, and not on+behalf of any Contributor. You must make it absolutely clear that any+such warranty, support, indemnity, or liability obligation is offered by+You alone, and You hereby agree to indemnify every Contributor for any+liability incurred by such Contributor as a result of warranty, support,+indemnity or liability terms You offer. You may include additional+disclaimers of warranty and limitations of liability specific to any+jurisdiction.++4. Inability to Comply Due to Statute or Regulation+---------------------------------------------------++If it is impossible for You to comply with any of the terms of this+License with respect to some or all of the Covered Software due to+statute, judicial order, or regulation then You must: (a) comply with+the terms of this License to the maximum extent possible; and (b)+describe the limitations and the code they affect. Such description must+be placed in a text file included with all distributions of the Covered+Software under this License. Except to the extent prohibited by statute+or regulation, such description must be sufficiently detailed for a+recipient of ordinary skill to be able to understand it.++5. Termination+--------------++5.1. The rights granted under this License will terminate automatically+if You fail to comply with any of its terms. However, if You become+compliant, then the rights granted under this License from a particular+Contributor are reinstated (a) provisionally, unless and until such+Contributor explicitly and finally terminates Your grants, and (b) on an+ongoing basis, if such Contributor fails to notify You of the+non-compliance by some reasonable means prior to 60 days after You have+come back into compliance. Moreover, Your grants from a particular+Contributor are reinstated on an ongoing basis if such Contributor+notifies You of the non-compliance by some reasonable means, this is the+first time You have received notice of non-compliance with this License+from such Contributor, and You become compliant prior to 30 days after+Your receipt of the notice.++5.2. If You initiate litigation against any entity by asserting a patent+infringement claim (excluding declaratory judgment actions,+counter-claims, and cross-claims) alleging that a Contributor Version+directly or indirectly infringes any patent, then the rights granted to+You by any and all Contributors for the Covered Software under Section+2.1 of this License shall terminate.++5.3. In the event of termination under Sections 5.1 or 5.2 above, all+end user license agreements (excluding distributors and resellers) which+have been validly granted by You or Your distributors under this License+prior to termination shall survive termination.++************************************************************************+*                                                                      *+*  6. Disclaimer of Warranty                                           *+*  -------------------------                                           *+*                                                                      *+*  Covered Software is provided under this License on an "as is"       *+*  basis, without warranty of any kind, either expressed, implied, or  *+*  statutory, including, without limitation, warranties that the       *+*  Covered Software is free of defects, merchantable, fit for a        *+*  particular purpose or non-infringing. The entire risk as to the     *+*  quality and performance of the Covered Software is with You.        *+*  Should any Covered Software prove defective in any respect, You     *+*  (not any Contributor) assume the cost of any necessary servicing,   *+*  repair, or correction. This disclaimer of warranty constitutes an   *+*  essential part of this License. No use of any Covered Software is   *+*  authorized under this License except under this disclaimer.         *+*                                                                      *+************************************************************************++************************************************************************+*                                                                      *+*  7. Limitation of Liability                                          *+*  --------------------------                                          *+*                                                                      *+*  Under no circumstances and under no legal theory, whether tort      *+*  (including negligence), contract, or otherwise, shall any           *+*  Contributor, or anyone who distributes Covered Software as          *+*  permitted above, be liable to You for any direct, indirect,         *+*  special, incidental, or consequential damages of any character      *+*  including, without limitation, damages for lost profits, loss of    *+*  goodwill, work stoppage, computer failure or malfunction, or any    *+*  and all other commercial damages or losses, even if such party      *+*  shall have been informed of the possibility of such damages. This   *+*  limitation of liability shall not apply to liability for death or   *+*  personal injury resulting from such party's negligence to the       *+*  extent applicable law prohibits such limitation. Some               *+*  jurisdictions do not allow the exclusion or limitation of           *+*  incidental or consequential damages, so this exclusion and          *+*  limitation may not apply to You.                                    *+*                                                                      *+************************************************************************++8. Litigation+-------------++Any litigation relating to this License may be brought only in the+courts of a jurisdiction where the defendant maintains its principal+place of business and such litigation shall be governed by laws of that+jurisdiction, without reference to its conflict-of-law provisions.+Nothing in this Section shall prevent a party's ability to bring+cross-claims or counter-claims.++9. Miscellaneous+----------------++This License represents the complete agreement concerning the subject+matter hereof. If any provision of this License is held to be+unenforceable, such provision shall be reformed only to the extent+necessary to make it enforceable. Any law or regulation which provides+that the language of a contract shall be construed against the drafter+shall not be used to construe this License against a Contributor.++10. Versions of the License+---------------------------++10.1. New Versions++Mozilla Foundation is the license steward. Except as provided in Section+10.3, no one other than the license steward has the right to modify or+publish new versions of this License. Each version will be given a+distinguishing version number.++10.2. Effect of New Versions++You may distribute the Covered Software under the terms of the version+of the License under which You originally received the Covered Software,+or under the terms of any subsequent version published by the license+steward.++10.3. Modified Versions++If you create software not governed by this License, and you want to+create a new license for such software, you may create and use a+modified version of this License if you rename the license and remove+any references to the name of the license steward (except to note that+such modified license differs from this License).++10.4. Distributing Source Code Form that is Incompatible With Secondary+Licenses++If You choose to distribute Source Code Form that is Incompatible With+Secondary Licenses under the terms of this version of the License, the+notice described in Exhibit B of this License must be attached.++Exhibit A - Source Code Form License Notice+-------------------------------------------++  This Source Code Form is subject to the terms of the Mozilla Public+  License, v. 2.0. If a copy of the MPL was not distributed with this+  file, You can obtain one at http://mozilla.org/MPL/2.0/.++If it is not possible or desirable to put the notice in a particular+file, then You may include the notice in a location (such as a LICENSE+file in a relevant directory) where a recipient would be likely to look+for such a notice.++You may add additional accurate notices of copyright ownership.
+ README.md view
@@ -0,0 +1,97 @@+# Amazon IAM Policy Documents++* [Description](#description)+* [Example](#example)+* [Contribute](#contribute)+* [Licence](#licence)+++## Description++This library provides data types and combinators that allow you to declare,+encode, and decode the [IAM JSON policy](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies.html)+language with a modicum of safety, minus any extreme type-level features.++The IAM policy documents can be safely constructed via the provided datatypes+and mapped, folded, and traversed via the provided instances, combinators,+and lenses. The resulting structure can then be encoded as a valid IAM JSON+policy document for using with Amazon IAM and related services.++The details of what goes into a policy vary for each service, depending on what+actions the service makes available, what types of resources it contains, and+so on. When you're writing policies for a specific service, it's helpful to see+examples of policies for that service. View the [AWS Services That Work with IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html)+documentation for more information.+++## Example++The following example sets up S3 bucket management:++```haskell+{-# LANGUAGE OverloadedLists   #-}+{-# LANGUAGE OverloadedStrings #-}++module Main (main) where++import qualified Amazonka-Iam-Policy.IAM.Policy as Policy++main :: IO ()+main =+    print . Policy.encode $+        Policy.document+            [ Policy.allow+                { Policy.action   = Policy.some ["s3:*"]+                , Policy.resource =+                    Policy.some+                        [ "arn:aws:s3:::<BUCKET-NAME>"+                        , "arn:aws:s3:::<BUCKET-NAME>/*"+                        ]+                }+            , Policy.deny+                { Policy.action   = Policy.not ["s3:*"]+                , Policy.resource =+                    Policy.not+                        [+                        , "arn:aws:s3:::<BUCKET-NAME>"+                        , "arn:aws:s3:::<BUCKET-NAME>/*"+                        ]+                }+            ]+```++Resulting in the following encoded IAM JSON policy document:++```json+{+  "Version": "2012-10-17",+  "Statement": [+    {+      "Effect": "Allow",+      "Action": "s3:*",+      "Resource": [+        "arn:aws:s3:::<BUCKET-NAME>",+        "arn:aws:s3:::<BUCKET-NAME>/*"+      ]+    },+    {+      "Effect": "Deny",+      "NotAction": "s3:*",+      "NotResource": [+        "arn:aws:s3:::<BUCKET-NAME>",+        "arn:aws:s3:::<BUCKET-NAME>/*"+      ]+    }+  ]+}+```+++## Contribute++For any problems, comments, or feedback please create an issue [here on GitHub](https://github.com/brendanhay/amazonka-iam-policy/issues).+++## Licence++`amazonka-iam-policy` is released under the [Mozilla Public License Version 2.0](http://www.mozilla.org/MPL/).
+ amazonka-iam-policy.cabal view
@@ -0,0 +1,97 @@+name:                  amazonka-iam-policy+version:               0.0.1+synopsis:              Amazon IAM Policy Document DSL and Combinators.+homepage:              https://github.com/brendanhay/amazonka-iam-policy+bug-reports:           https://github.com/brendanhay/amazonka-iam-policy/issues+license:               MPL-2.0+license-file:          LICENSE+author:                Brendan Hay+maintainer:            Brendan Hay <brendan.g.hay+amazonka@gmail.com>+copyright:             Copyright (c) 2017 Brendan Hay+category:              Network, AWS, Cloud+build-type:            Simple+extra-source-files:    README.md CHANGELOG.md+cabal-version:         >= 1.10+data-files:            test/golden/*.json++description:+    This library provides data types and combinators that allow you to declare,+    encode, and decode the <https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies.html IAM JSON policy>+    language with a modicum of safety, minus any extreme type-level features.+    .+    The IAM policy documents can be safely constructed via the provided datatypes+    and mapped, folded, and traversed via the provided instances, combinators,+    and lenses. The resulting structure can then be encoded as a valid IAM JSON+    policy document for using with Amazon IAM and related services.+    .+    The details of what goes into a policy vary for each service, depending on what+    actions the service makes available, what types of resources it contains, and+    so on. When you're writing policies for a specific service, it's helpful to see+    examples of policies for that service. View the+    <https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html AWS Services That Work with IAM> documentation for more information.+    .+    See "Amazonka.IAM.Policy" to get started.++source-repository head+    type:              git+    location:          git://github.com/brendanhay/amazonka-iam-policy.git+    subdir:            amazonka-iam-policy++library+    default-language:  Haskell2010+    hs-source-dirs:    src++    ghc-options:+        -Wall+        -fwarn-incomplete-uni-patterns+        -fwarn-incomplete-record-updates+        -funbox-strict-fields++    exposed-modules:+        Amazonka.IAM.Policy+        Amazonka.IAM.Policy.Lens++    build-depends:+          aeson+        , base >= 4.7 && < 5+        , base64-bytestring+        , bytestring+        , profunctors+        , scientific+        , text+        , time++test-suite doctest+    type:              exitcode-stdio-1.0+    default-language:  Haskell2010+    hs-source-dirs:    test+    main-is:           Doctest.hs++    ghc-options:       -Wall -threaded++    other-modules:+        Paths_amazonka_iam_policy++    build-depends:+          aeson-pretty+        , amazonka-iam-policy+        , doctest+        , base++test-suite golden+    type:              exitcode-stdio-1.0+    default-language:  Haskell2010+    hs-source-dirs:    test+    main-is:           Golden.hs++    ghc-options:       -Wall -threaded++    other-modules:+        Paths_amazonka_iam_policy++    build-depends:+          aeson+        , amazonka-iam-policy+        , base+        , bytestring+        , hspec
+ src/Amazonka/IAM/Policy.hs view
@@ -0,0 +1,1263 @@+{-# LANGUAGE DeriveFoldable             #-}+{-# LANGUAGE DeriveFunctor              #-}+{-# LANGUAGE DeriveTraversable          #-}+{-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase                 #-}+{-# LANGUAGE OverloadedLists            #-}+{-# LANGUAGE OverloadedStrings          #-}+{-# LANGUAGE RecordWildCards            #-}+{-# LANGUAGE TupleSections              #-}+{-# LANGUAGE TypeFamilies               #-}+{-# LANGUAGE ViewPatterns               #-}++{- | This module provides data types and combinators that allow you to declare,+encode, and decode the+<https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies.html IAM JSON policy>+language. The available 'Action' and 'Condition' keys can be found under the+<https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_actionsconditions.html AWS Service Actions and Condition Context Keys for Use in IAM policies>+documentation.++Lenses and prisms for those wanting to traverse and manipulate policy documents+and their constituent elements can be found in the "Amazonka.IAM.Policy.Lens"+module.+-}+module Amazonka.IAM.Policy+    (+    -- * Usage+    -- $usage++    -- * Policy Documents+      Policy    (..)++    -- ** Constructing Policies+    , document+    , singleton++    -- ** Encoding+    , encode++    -- * Language Elements+    -- $elements++    , Block     (..)+    , Match     (..)++    -- ** Specifying and Negating Elements+    , some+    , not+    , any+    , none++    -- ** Version+    , Version   (..)++    -- ** Id+    , Id        (..)++    -- ** Statement+    , Statement (..)++    -- *** Constructing Statements+    , allow+    , deny++    -- ** Sid+    , Sid       (..)++    -- ** Principal+    , Principal (..)++    -- ** Effect+    , Effect    (..)++    -- ** Action+    , Action    (..)++    -- ** Resource+    , Resource  (..)++    -- ** Condition+    , Key       (..)+    , Condition (..)+    ) where++import Prelude hiding (any, id, not)++import Control.Applicative (optional, (<|>))++import Data.Aeson         (FromJSON, ToJSON (toJSON), (.:), (.:?), (.=))+import Data.Bifunctor     (second)+import Data.ByteString    (ByteString)+import Data.Function      (on)+import Data.List.NonEmpty (NonEmpty)+import Data.Maybe         (catMaybes)+import Data.Scientific    (Scientific, scientificP)+import Data.Semigroup     (Semigroup ((<>)))+import Data.String        (IsString)+import Data.Text          (Text)+import Data.Time          (UTCTime)++import GHC.Exts (IsList (..))++import qualified Data.Aeson                   as JSON+import qualified Data.Aeson.Types             as JSON+import qualified Data.ByteString.Base64       as Base64+import qualified Data.ByteString.Lazy         as LBS+import qualified Data.Text.Encoding           as Text+import qualified Data.Time.Clock.POSIX        as POSIX+import qualified Data.Time.Format             as Time+import qualified Text.ParserCombinators.ReadP as Read++{- $setup++>>> :set -XOverloadedStrings++>>> import Data.Aeson.Encode.Pretty (encodePretty)+>>> import Data.ByteString.Lazy.Char8 (unpack)++>>> let encode' = putStrLn . unpack . encodePretty++-}++{- $usage+It's recommended that you import and use this module qualified to avoid any+ambiguity with prelude functions.++The following is an example of using the 'Semigroup' instance of 'Policy' to+construct a document from multiple statements. This example creates a policy+that would allow an IAM user sufficient privilege to rotate their credentials:++@+&#x7b;-\# LANGUAGE OverloadedStrings \#-&#x7d;++import qualified Amazonka.IAM.Policy as Policy++ ( Policy.singleton+       (Policy.allow+           { Policy.action   =+               Policy.some+                   [ "iam:ListUsers"+                   , "iam:GetAccountPasswordPolicy"+                   ]+           , Policy.resource = Policy.any+           })++<> Policy.singleton+       (Policy.allow+           { Policy.action   =+               Policy.some+                   [ "iam:*AccessKey*"+                   , "iam:ChangePassword"+                   , "iam:GetUser"+                   , "iam:*ServiceSpecificCredential*"+                   , "iam:*SigningCertificate*"+                   ]+           , Policy.resource =+               Policy.some+                   [ "arn:aws:iam::*:user/${aws:username}"+                   ]+           })+ )+@++Which results in the following encoded IAM JSON policy document:++> {+>   "Version": "2012-10-17",+>   "Statement": [+>     {+>       "Effect": "Allow",+>       "Action": [+>         "iam:ListUsers",+>         "iam:GetAccountPasswordPolicy"+>       ],+>       "Resource": "*"+>     },+>     {+>       "Effect": "Allow",+>       "Action": [+>         "iam:*AccessKey*",+>         "iam:ChangePassword",+>         "iam:GetUser",+>         "iam:*ServiceSpecificCredential*",+>         "iam:*SigningCertificate*"+>       ],+>       "Resource": "arn:aws:iam::*:user/${aws:username}"+>     }+>   ]+> }++You can also use the @OverloadedLists@ extension with the 'document' smart constructor+to create a 'Policy' using the 'IsList' instance for 'NonEmpty'. The following example+sets up S3 bucket management:++@+&#x7b;-\# LANGUAGE OverloadedLists   \#-&#x7d;+&#x7b;-\# LANGUAGE OverloadedStrings \#-&#x7d;++import qualified Amazonka.IAM.Policy as Policy++Policy.document+    [ Policy.allow+        { Policy.action   = Policy.some ["s3:*"]+        , Policy.resource =+            Policy.some+                [ "arn:aws:s3:::<BUCKET-NAME>"+                , "arn:aws:s3:::<BUCKET-NAME>/*"+                ]+        }+    , Policy.deny+        { Policy.action   = Policy.not ["s3:*"]+        , Policy.resource =+            Policy.not+                [+                , "arn:aws:s3:::<BUCKET-NAME>"+                , "arn:aws:s3:::<BUCKET-NAME>/*"+                ]+        }+    ]+@++Resulting in the following encoded IAM JSON policy document:++> {+>   "Version": "2012-10-17",+>   "Statement": [+>     {+>       "Effect": "Allow",+>       "Action": "s3:*",+>       "Resource": [+>         "arn:aws:s3:::<BUCKET-NAME>",+>         "arn:aws:s3:::<BUCKET-NAME>/*"+>       ]+>     },+>     {+>       "Effect": "Deny",+>       "NotAction": "s3:*",+>       "NotResource": [+>         "arn:aws:s3:::<BUCKET-NAME>",+>         "arn:aws:s3:::<BUCKET-NAME>/*"+>       ]+>     }+>   ]+> }++Please be aware that the use of @OverloadedLists@ and 'NonEmpty' will error if+'document' is passed an empty list.+-}++{- $elements+IAM JSON policy documents are made up of elements. The elements are listed here+roughly in the general order you use them in a policy.++The details of what goes into a policy vary for each service, depending on what+actions the service makes available, what types of resources it contains, and+so on. When you're writing policies for a specific service, it's helpful to see+examples of policies for that service. View the <https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html AWS Services That Work with IAM>+documentation for more information.++This library attempts to stick as closely as possible to the IAM policy language grammar+and its specific terminology while providing a modicum of safety without going+overboard with type-level features. To support this, the concept of @Element@ vs+@NotElement@ and @"*"@ wildcards are generalised by the introduction of two+additional types not specified directly in the <https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_grammar.html language grammar>.++Firstly there is the 'Block' type which allows us to negate @Principal@,+@Action@, and @Resource@ elements via the related @NotPrincipal@, @NotAction@,+and @NotResource@ elements. Secondly the @Match@ type provides a way to+generally specific @Some@ values for an element, or the @Wildcard@ match+equivalent to @"*"@. Combinators are then provided for the common cases.++==== Example++>>> encode' $ singleton (allow { action = some ["foo", "bar"] })+...+            "Effect": "Allow",+            "Action": [+                "foo",+                "bar"+            ],+            "Resource": []+...++>>> encode' $ singleton (allow { action = not ["baz"] })+...+            "Effect": "Allow",+            "Resource": [],+            "NotAction": "baz"+...++>>> encode' $ singleton (allow { action = any })+...+            "Effect": "Allow",+            "Action": "*",+            "Resource": []+...++>>> encode' $ singleton (allow { action = none })+...+            "Effect": "Allow",+            "Resource": [],+            "NotAction": "*"+...++The above examples work identically for the 'action', 'resource', and+'principal' statement elements.++-}++{- | A 'Block' is a shared type denoting blocks of the policy language grammar.++This is used by the 'Principal', 'Action', 'Resource' elements of the policy+language.++==== Grammar++> <name_block> = ("Name" | "NotName") : <match_block>+-}+data Block a+    = Match !(Match a)+    | Not   !(Match a)+      deriving (Show, Eq, Functor, Foldable, Traversable)++blockToJSON :: ToJSON a => Text -> Block a -> JSON.Pair+blockToJSON k = \case+    Match v -> k .= v+    Not   v -> ("Not" <> k) .= v+{-# INLINE blockToJSON #-}++blockParseJSON :: FromJSON a => Text -> JSON.Object -> JSON.Parser (Block a)+blockParseJSON k o =+        Match <$> o .: k+    <|> Not   <$> o .: ("Not" <> k)+{-# INLINE blockParseJSON #-}++-- | Match one or more elements.+some :: a -> Block a+some = Match . Some+{-# INLINEABLE some #-}++-- | Negate a match of one or more elements.+--+-- `not` is the negation of `some`.+not :: a -> Block a+not = Not . Some+{-# INLINEABLE not #-}++-- | Match any element with a wildcard.+any :: Block a+any = Match Wildcard+{-# INLINEABLE any #-}++-- | Match no element with a negated wildcard.+--+-- `none` is the negation of `any`.+none :: Block a+none = Not Wildcard+{-# INLINEABLE none #-}++{- | A 'Match' is a shared type used to denote wildcards in the policy+language grammar.++This is used by the 'Principal', 'Action', 'Resource' elements of the policy+language.++==== Grammar++> <match_block> = ("*" | <item>)+-}+data Match a+    = Some !a+    | Wildcard+      deriving (Show, Eq, Functor, Foldable, Traversable)++instance Semigroup a => Semigroup (Match a) where+    (<>) a b =+        case (a, b) of+            (Wildcard, _)        -> Wildcard+            (_,        Wildcard) -> Wildcard+            (Some xs,  Some ys)  -> Some (xs <> ys)+    {-# INLINEABLE (<>) #-}++instance (Semigroup a, Monoid a) => Monoid (Match a) where+    mempty = Some mempty+    {-# INLINEABLE mempty #-}++    mappend = (<>)+    {-# INLINEABLE mappend #-}++instance ToJSON a => ToJSON (Match a) where+    toJSON = \case+        Some x   -> toJSON x+        Wildcard -> JSON.String "*"+    {-# INLINE toJSON #-}++instance FromJSON a => FromJSON (Match a) where+    parseJSON = \case+        JSON.String "*"  -> pure Wildcard+        JSON.Array ["*"] -> pure Wildcard+        x                -> Some <$> JSON.parseJSON x+    {-# INLINE parseJSON #-}++-- | This type is used to provide support for interchangeably encoding or parsing+-- a single value, or multiple values as a list.+data OneOrMany a+    = One  !a+    | Many ![a]+      deriving (Show, Eq)++instance ToJSON a => ToJSON (OneOrMany a) where+    toJSON = \case+        One  x  -> toJSON x+        Many xs -> toJSON xs+    {-# INLINE toJSON #-}++instance FromJSON a => FromJSON (OneOrMany a) where+    parseJSON o = One <$> JSON.parseJSON o <|> Many <$> JSON.parseJSON o+    {-# INLINE parseJSON #-}++instance IsList (OneOrMany a) where+    type Item (OneOrMany a) = a++    toList = \case+        One  x -> [x]+        Many xs -> xs+    {-# INLINE toList #-}++    fromList = \case+        [x] -> One  x+        xs  -> Many xs+    {-# INLINE fromList #-}++{- | A policy document is a non-empty list of IAM statements with a supported version.++The statements of a policy document can be traversed and manipulated via the+'Functor', 'Foldable', and 'Traverseable' instances. You can use the+'Semigroup' instance to concatenate multiple 'Policy' documents together,+preserving the highest supported version and first encountered 'Id'.++==== Grammar++> policy  = {+>     <version_block?>+>     <id_block?>+>     <statement_block>+> }++-}+data Policy a = Policy+    { version   :: !(Maybe Version)+    , id        :: !(Maybe Id)+    , statement :: !(NonEmpty a)+    } deriving (Show, Eq, Functor, Foldable, Traversable)++-- FIXME: Note about pointwise 'First' behaviour of version/id.+instance Semigroup (Policy a) where+    (<>) a b = Policy+        { version   = on (<>)  version   a b+        , id        = on (<|>) id        a b+        , statement = on (<>)  statement a b+        }+    {-# INLINEABLE (<>) #-}++instance ToJSON a => ToJSON (Policy a) where+    toJSON Policy{..} =+        JSON.object $ catMaybes+            [ fmap ("Id"        .=) id+            , fmap ("Version"   .=) version+            , Just ("Statement" .=  statement)+            ]+    {-# INLINEABLE toJSON #-}++instance FromJSON a => FromJSON (Policy a) where+    parseJSON = JSON.withObject "Policy" $ \o -> do+        version   <- o .:? "Version"+        id        <- o .:? "Id"+        statement <- o .:  "Statement"+        pure Policy{..}+    {-# INLINEABLE parseJSON #-}++{- | Construct a policy from a collection of 'NonEmpty' 'Statement's using the+ default @2012-10-17@ version and no identifier.++==== Example++>>> encode' $ document (pure allow <> pure deny)+{+    "Statement": [+        {+            "Effect": "Allow",+            "Action": [],+            "Resource": []+        },+        {+            "Effect": "Deny",+            "Action": [],+            "Resource": []+        }+    ],+    "Version": "2012-10-17"+}++-}+document :: NonEmpty a -> Policy a+document xs = Policy+    { version   = Just Version_2012_10_17+    , id        = Nothing+    , statement = xs+    }+{-# INLINEABLE document #-}++{- | Construct a policy from a single 'Statement' using the default+@2012-10-17@ version and no identifier.++==== Example++>>> encode' $ singleton (allow { resource = any })+{+    "Statement": [+        {+            "Effect": "Allow",+            "Action": [],+            "Resource": "*"+        }+    ],+    "Version": "2012-10-17"+}++-}+singleton :: a -> Policy a+singleton = document . pure+{-# INLINEABLE singleton #-}++{- | Encode the IAM policy document as JSON.++==== Example++>>> encode $ singleton allow+"{\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[],\"Resource\":[]}],\"Version\":\"2012-10-17\"}"++-}+encode :: Policy Statement -> LBS.ByteString+encode = JSON.encode+{-# INLINEABLE encode #-}++{- | The 'Version' elements specifies the language syntax rules that are to be+used to process this policy. If you include features that are not available in+the specified version, then your policy will generate errors or not work the+way you intend. As a general rule, you should specify the most recent version+available, unless you depend on a feature that was deprecated in later+versions.++==== Grammar++> <version_block> = "Version" : ("2008-10-17" | "2012-10-17")++==== Example++>>> JSON.encode Version_2012_10_17+"\"2012-10-17\""++-}+data Version+    = Version_2008_10_17+    | Version_2012_10_17+      deriving (Show, Eq, Ord)++-- | '(<>)' always chooses the newest version.+instance Semigroup Version where+    (<>) = max+    {-# INLINEABLE (<>) #-}++instance ToJSON Version where+    toJSON = \case+        Version_2008_10_17 -> JSON.String "2008-10-17"+        Version_2012_10_17 -> JSON.String "2012-10-17"+    {-# INLINEABLE toJSON #-}++instance FromJSON Version where+    parseJSON = JSON.withText "Version" $ \case+        "2008-10-17" -> pure Version_2008_10_17+        "2012-10-17" -> pure Version_2012_10_17+        x            -> fail ("Unabled to parse Version from " ++ show x)+    {-# INLINEABLE parseJSON #-}++{- | The 'Id' element specifies an optional identifier for the policy. The ID+is used differently in different services.++For services that let you set an ID element, we recommend you use a UUID+(GUID) for the value, or incorporate a UUID as part of the ID to ensure+uniqueness.++/Note/: Some AWS services (for example, Amazon SQS or Amazon SNS) might+require this element and have uniqueness requirements for it. For+service-specific information about writing policies, refer to the+documentation for the service you're working with.++Use the 'id' record field to set the 'Id' of a 'Policy' document.++==== Grammar++> <id_block> = "Id" : <policy_id_string>++==== Example++>>> encode' $ (singleton allow) { id = Just "123" }+{+...+    "Id": "123"+}++-}+newtype Id = Id { fromId :: Text }+    deriving (Show, Eq, Ord, ToJSON, FromJSON, IsString)++{- | The 'Statement' element is the main element for a policy. This element is+required. It can include multiple elements (see the subsequent sections in this+page). The Statement element contains an array of individual statements.++==== Grammar++> <statement_block> = "Statement" : [ <statement>, <statement>, ... ]+>+> <statement> = {+>     <sid_block?>,+>     <principal_block?>,+>     <effect_block>,+>     <action_block>,+>     <resource_block>,+>     <condition_block?>+> }++-}+data Statement = Statement+    { sid       :: !(Maybe Sid)+    , principal :: !(Maybe (Block Principal))+    , effect    :: !Effect+    , action    :: !(Block [Action])+    , resource  :: !(Block [Resource])+    , condition :: !(Maybe Condition)+    } deriving (Show, Eq)++instance ToJSON Statement where+    toJSON Statement{..} =+        let oneOrMany = fmap (fromList :: [a] -> OneOrMany a)+         in JSON.object $ catMaybes+            [ fmap ("Sid" .=) sid+            , fmap (blockToJSON "Principal") principal+            , Just ("Effect" .= effect)+            , Just (blockToJSON "Action"   $ oneOrMany action)+            , Just (blockToJSON "Resource" $ oneOrMany resource)+            , fmap ("Condition" .=) condition+            ]+    {-# INLINEABLE toJSON #-}++instance FromJSON Statement where+    parseJSON = JSON.withObject "Statement" $ \o -> do+        sid       <- o .:? "Sid"+        principal <- optional (blockParseJSON "Principal" o)+        effect    <- o .:  "Effect"+        action    <- oneOrMany <$> blockParseJSON "Action"   o+        resource  <- oneOrMany <$> blockParseJSON "Resource" o+        condition <- o .:? "Condition"+        pure Statement{..}+      where+        oneOrMany = fmap (toList :: OneOrMany a -> [a])+    {-# INLINEABLE parseJSON #-}++-- | Create a new statement with the effect set to 'Allow'.+allow :: Statement+allow = Statement+    { sid       = Nothing+    , principal = Nothing+    , effect    = Allow+    , action    = Match mempty+    , resource  = Match mempty+    , condition = Nothing+    }+{-# INLINEABLE allow #-}++-- | Create a new statement with the effect set to 'Deny'.+deny :: Statement+deny = allow { effect = Deny }+{-# INLINEABLE deny #-}++{- | The 'Sid' (statement ID) is an optional identifier that you provide for the+policy statement. You can assign a Sid value to each statement in a statement+array. In services that let you specify an ID element, such as SQS and SNS, the+Sid value is just a sub-ID of the policy document's ID. In IAM, the Sid value+must be unique within a JSON policy.++In IAM, the Sid is not exposed in the IAM API. You can't retrieve a+particular statement based on this ID.++/Note/: Some AWS services (for example, Amazon SQS or Amazon SNS) might require this+element and have uniqueness requirements for it. For service-specific+information about writing policies, refer to the documentation for the service+you're working with.++Use the 'sid' record field to set the 'Sid' of a 'Statement'.++==== Grammar++> <sid_block> = "Sid" : <sid_string>++==== Example++>>> encode' $ singleton (allow { sid = Just "cd3ad3d9-2776-4ef1-a904-4c229d1642ee" })+{+    "Statement": [+        {+            "Effect": "Allow",+            "Action": [],+            "Resource": [],+            "Sid": "cd3ad3d9-2776-4ef1-a904-4c229d1642ee"+        }+    ],+    "Version": "2012-10-17"+}++-}+newtype Sid = Sid { fromSid :: Text }+    deriving (Show, Eq, Ord, ToJSON, FromJSON, IsString)++{- | The 'Effect' element is required and specifies whether the statement+results in an allow or an explicit deny.++By default, access to resources is denied. To allow access to a resource,+you must set the Effect element to 'Allow'. To override an allow (for example,+to override an allow that is otherwise in force), you set the Effect element+to 'Deny'.++See the 'allow' and 'deny' smart constructors to create a 'Statement' with the+desired 'Effect' set.++==== Grammar++> <effect_block> = "Effect" : ("Allow" | "Deny")+-}+data Effect = Allow | Deny+    deriving (Show, Eq, Ord, Enum)++instance ToJSON Effect where+    toJSON = \case+        Allow -> "Allow"+        Deny  -> "Deny"+    {-# INLINEABLE toJSON #-}++instance FromJSON Effect where+    parseJSON = JSON.withText "Effect" $ \case+        "Allow" -> pure Allow+        "Deny"  -> pure Deny+        x       -> fail ("Unabled to parse Effect from " ++ show x)+    {-# INLINEABLE parseJSON #-}++{- | Use the 'Principal' element to specify the user (IAM user, federated user, or+assumed-role user), AWS account, AWS service, or other principal entity that+is allowed or denied access to a resource. You use the Principal element in+the trust policies for IAM roles and in resource-based policies—that is, in+policies that you embed directly in a resource. For example, you can embed+such policies in an Amazon S3 bucket, an Amazon Glacier vault, an Amazon SNS+topic, an Amazon SQS queue, or an AWS KMS customer master key (CMK).++Use the Principal element in these ways:++In IAM roles, use the Principal element in the role's trust policy to+specify who can assume the role. For cross-account access, you must specify+the 12-digit identifier of the trusted account.++/Note/: After you create the role, you can change the account to "*" to+allow everyone to assume the role. If you do this, we strongly recommend+that you limit who can access the role through other means, such as a+Condition element that limits access to only certain IP addresses. Do not+leave your role accessible to everyone!++In resource-based policies, use the 'Principal' element to specify the+accounts or users who are allowed to access the resource.++You can use 'Not' to negate the meaning of a 'Principal' element.++==== Grammar++> <principal_block> = ("Principal" | "NotPrincipal") : ("*" | <principal_map>)+>+> <principal_map> = { <principal_map_entry>, <principal_map_entry>, ... }+>+> <principal_map_entry> = ("AWS" | "Federated" | "Service") :+>     [<principal_id_string>, <principal_id_string>, ...]++==== Example++>>> encode' $ singleton (allow { principal = Just $ not Everyone })+{+    "Statement": [+        {+            "Effect": "Allow",+            "NotPrincipal": "*",+            "Action": [],+            "Resource": []+        }+    ],+    "Version": "2012-10-17"+}++>>> encode' $ singleton (allow { principal = Just $ some (AWS (pure "arn:foo:::bar")) })+{+    "Statement": [+        {+            "Effect": "Allow",+            "Action": [],+            "Principal": {+                "AWS": [+                    "arn:foo:::bar"+                ]+            },+            "Resource": []+        }+    ],+    "Version": "2012-10-17"+}++-}+data Principal+    = Everyone+    | AWS           !(NonEmpty Text)+    | Federated     !Text+    | Service       !(NonEmpty Text)+    | CanonicalUser !Text+      deriving (Show, Eq)++instance ToJSON Principal where+    toJSON = \case+        Everyone         -> JSON.String "*"+        AWS           ks -> JSON.object ["AWS"           .= ks]+        Federated     k  -> JSON.object ["Federated"     .= k]+        Service       ks -> JSON.object ["Service"       .= ks]+        CanonicalUser k  -> JSON.object ["CanonicalUser" .= k]+    {-# INLINEABLE toJSON #-}++instance FromJSON Principal where+    parseJSON v = everyone v <|> nested v+      where+        everyone =+            JSON.withText "Principal:*" $ \case+                "*" -> pure Everyone+                x   -> fail ("Unable to parse Principal:* from " ++ show x)++        nested =+            JSON.withObject "Principal" $ \o ->+                    AWS           <$> o .: "AWS"+                <|> Federated     <$> o .: "Federated"+                <|> Service       <$> o .: "Service"+                <|> CanonicalUser <$> o .: "CanonicalUser"+    {-# INLINEABLE parseJSON #-}++{- | The 'Action' element describes the specific action or actions that will be+allowed or denied. Statements must include either an Action or NotAction+element. Each AWS service has its own set of actions that describe tasks that+you can perform with that service.++You can use 'Not' to negate the meaning of a list of 'Action' elements.++==== Grammar++> <action_block> = ("Action" | "NotAction") :+>     ("*" | [<action_string>, <action_string>, ...])++-}+newtype Action = Action Text+    deriving (Show, Eq, ToJSON, FromJSON, IsString)++{- | The 'Resource' element specifies the object or objects that the statement+covers. Statements must include either a Resource or a NotResource element.++Each service has its own set of resources. Although you always use an ARN to+specify a resource, the details of the ARN for a resource depend on the+service and the resource. For information about how to specify a resource,+refer to the documentation for the service whose resources you're writing a+statement for.++/Note:/ Some services do not let you specify actions for individual+resources; instead, any actions that you list in the Action or NotAction+element apply to all resources in that service. In these cases, you use the+wildcard @"*"@ in the Resource element.++You can use 'Not' to negate the meaning of a list of 'Resource' elements.++==== Grammar++> <resource_block> = ("Resource" | "NotResource") :+>     ("*" | [<resource_string>, <resource_string>, ...])++-}+newtype Resource = Resource Text+    deriving (Show, Eq, ToJSON, FromJSON, IsString)++-- | A key that will be tested as the target of a 'Condition'.+newtype Key = Key { fromKey :: Text }+    deriving (Show, Eq, FromJSON, ToJSON, IsString)++{- | The 'Condition' element (or Condition block) lets you specify conditions+for when a policy is in effect. The Condition element is optional. In the+Condition element, you build expressions in which you use condition operators+(equal, less than, etc.) to match the condition in the policy against values in+the request. Condition values can include date, time, the IP address of the+requester, the ARN of the request source, the user name, user ID, and the user+agent of the requester. Some services let you specify additional values in+conditions; for example, Amazon S3 lets you write a condition using the+@s3:VersionId@ key, which is unique to that service.++    * __String:__+      String condition operators let you construct 'Condition' elements that+      restrict access based on comparing a key to a string value.++    * __Numeric:__+      Numeric condition operators let you construct Condition elements that+      restrict access based on comparing a key to an integer or decimal value.++    * __Date:__+      Date condition operators let you construct Condition elements that restrict+      access based on comparing a key to a date/time value. You use these condition+      operators with the aws:CurrentTime key or aws:EpochTime keys. You must specify+      date/time values with one of the W3C implementations of the ISO 8601 date+      formats or in epoch (UNIX) time.+      Wildcards are not permitted for date condition operators.++    * __Boolean:__+      Boolean conditions let you construct Condition elements that restrict access+      based on comparing a key to "true" or "false."++    * __Binary:__+      The BinaryEquals condition operator let you construct Condition elements+      that test key values that are in binary format. It compares the value of the+      specified key byte for byte against a base-64 encoded representation of the+      binary value in the policy.++    * __IP Address:__+      IP address condition operators let you construct Condition elements that+      restrict access based on comparing a key to an IPv4 or IPv6 address or range+      of IP addresses. You use these with the aws:SourceIp key. The value must be+      in the standard CIDR format (for example, 203.0.113.0/24 or+      2001:DB8:1234:5678::/64). If you specify an IP address without the+      associated routing prefix, IAM uses the default prefix value of /32.+      Some AWS services support IPv6, using :: to represent a range of 0s. To+      learn whether a service supports IPv6, see the documentation for that+      service.++    * __Amazon Resource Name (ARN):__+      Amazon Resource Name (ARN) condition operators let you construct Condition+      elements that restrict access based on comparing a key to an ARN. The ARN is+      considered a string. This value is available for only some services; not all+      services support request values that can be compared as ARNs.++    * __Key Existence:__+      You can add IfExists to the end of any condition operator name except the+      Null condition—for example, StringLikeIfExists. You do this to say "If the+      policy key is present in the context of the request, process the key as+      specified in the policy. If the key is not present, the condition evaluate+      the condition element as true." Other condition elements in the statement+      can still result in a nonmatch, but not a missing key when checked with+      ...IfExists.++    * __Null:__+      Use a Null condition operator to check if a condition key is present at the+      time of authorization. In the policy statement, use either true (the key+      doesn't exist — it is null) or false (the key exists and its value is not null).+      For example, you can use this condition operator to determine whether a user is+      using their own credentials for the operation or temporary credentials. If the+      user is using temporary credentials, then the key aws:TokenIssueTime exists and+      has a value. The following example shows a condition that states that the user+      must not be using temporary credentials (the key must not exist) for the user+      to use the Amazon EC2 API.++==== Grammar++> <condition_block> = "Condition" : { <condition_map> }+>+> <condition_map> {+>   <condition_type_string> : { <condition_key_string> : <condition_value_list> },+>   <condition_type_string> : { <condition_key_string> : <condition_value_list> }, ...+> }+>+> <condition_value_list> = [<condition_value>, <condition_value>, ...]+>+> <condition_value> = ("string" | "number" | "Boolean")++==== Example++>>> encode' $ singleton (allow { condition = Just (Bool "aws:MultiFactorAuthPresent" True) })+{+    "Statement": [+        {+            "Effect": "Allow",+            "Action": [],+            "Resource": [],+            "Condition": {+                "Bool": {+                    "aws:MultiFactorAuthPresent": true+                }+            }+        }+    ],+    "Version": "2012-10-17"+}++-}+data Condition+    = StringEquals              !Key !Text+    -- ^ Exact matching, case sensitive.+    | StringNotEquals           !Key !Text+    -- ^ Negated matching.+    | StringEqualsIgnoreCase    !Key !Text+    -- ^ Exact matching, ignoring case.+    | StringNotEqualsIgnoreCase !Key !Text+    -- ^ Negated matching, ignoring case+    | StringLike                !Key ![Text]+    -- ^ Case-sensitive matching. The values can include a multi-character match+    -- wildcard (*) or a single-character match wildcard (?) anywhere in the+    -- string.+    --+    -- If a key contains multiple values, StringLike can be qualified with set+    -- operators—ForAllValues:StringLike and ForAnyValue:StringLike. For more+    -- information, see Creating a Condition That Tests Multiple Key Values (Set+    -- Operations).+    | StringNotLike             !Key ![Text]+    -- ^ Negated case-sensitive matching. The values can include a multi-character+    -- match wildcard (*) or a single-character match wildcard (?) anywhere in the+    -- string.++    | NumericEquals             !Key !Scientific+    -- ^ Matching.+    | NumericNotEquals          !Key !Scientific+    -- ^ Negated matching.+    | NumericLessThan           !Key !Scientific+    -- ^ "Less than" matching.+    | NumericLessThanEquals     !Key !Scientific+    -- ^ "Less than or equals" matching.+    | NumericGreaterThan        !Key !Scientific+    -- ^ "Greater than" matching.+    | NumericGreaterThanEquals  !Key !Scientific+    -- ^ "Greater than or equals" matching.++    | DateEquals                !Key !UTCTime+    -- ^ Matching a specific date.+    | DateNotEquals             !Key !UTCTime+    -- ^ Negated matching.+    | DateLessThan              !Key !UTCTime+    -- ^ Matching before a specific date and time.+    | DateLessThanEquals        !Key !UTCTime+    -- ^ Matching at or before a specific date and time.+    | DateGreaterThan           !Key !UTCTime+    -- ^ Matching after a specific a date and time.+    | DateGreaterThanEquals     !Key !UTCTime+    -- ^ Matching at or after a specific date and time.++    | Bool                      !Key !Bool+    -- ^ Boolean matching.++    | BinaryEquals              !Key !ByteString+    -- ^ The BinaryEquals condition operator let you construct Condition elements+    -- that test key values that are in binary format. It compares the value of the+    -- specified key byte for byte against a base-64 encoded representation of the+    -- binary value in the policy.++    | IpAddress                 !Key !ByteString+    -- ^ The specified IP address or range.+    | NotIpAddress              !Key !ByteString+    -- ^ All IP addresses except the specified IP address or range.++    | ArnEquals                 !Key !Text+    -- ^ Case-sensitive matching of the ARN. Each of the six colon-delimited+    -- components of the ARN is checked separately and each can include a+    -- multi-character match wildcard (*) or a single-character match wildcard+    -- (?). These behave identically.+    | ArnLike                   !Key !Text+    -- ^ Case-sensitive matching of the ARN. Each of the six colon-delimited+    -- components of the ARN is checked separately and each can include a+    -- multi-character match wildcard (*) or a single-character match wildcard+    -- (?). These behave identically.+    | ArnNotEquals              !Key !Text+    -- ^ Negated matching for ARN. These behave identically.+    | ArnNotLike                !Key !Text+    -- ^ Negated matching for ARN. These behave identically.+      deriving (Show, Eq)++instance ToJSON Condition where+    toJSON x = JSON.object [op .= JSON.object [key .= val]]+      where+        (op, fromKey -> key, val) =+            case x of+                StringEquals              k v  ->+                    ("StringEquals",              k, toJSON v)+                StringNotEquals           k v  ->+                    ("StringNotEquals",           k, toJSON v)+                StringEqualsIgnoreCase    k v  ->+                    ("StringEqualsIgnoreCase",    k, toJSON v)+                StringNotEqualsIgnoreCase k v  ->+                    ("StringNotEqualsIgnoreCase", k, toJSON v)+                StringLike                k vs ->+                    ("StringLike",                k, toJSON vs)+                StringNotLike             k vs ->+                    ("StringNotLike",             k, toJSON vs)++                NumericEquals             k v  ->+                    ("NumericEquals",             k, toJSON v)+                NumericNotEquals          k v  ->+                    ("NumericNotEquals",          k, toJSON v)+                NumericLessThan           k v  ->+                    ("NumericLessThan",           k, toJSON v)+                NumericLessThanEquals     k v  ->+                    ("NumericLessThanEquals",     k, toJSON v)+                NumericGreaterThan        k v  ->+                    ("NumericGreaterThan",        k, toJSON v)+                NumericGreaterThanEquals  k v  ->+                    ("NumericGreaterThanEquals",  k, toJSON v)++                DateEquals                k v  ->+                    ("DateEquals",                k, toJSON v)+                DateNotEquals             k v  ->+                    ("DateNotEquals",             k, toJSON v)+                DateLessThan              k v  ->+                    ("DateLessThan",              k, toJSON v)+                DateLessThanEquals        k v  ->+                    ("DateLessThanEquals",        k, toJSON v)+                DateGreaterThan           k v  ->+                    ("DateGreaterThan",           k, toJSON v)+                DateGreaterThanEquals     k v  ->+                    ("DateGreaterThanEquals",     k, toJSON v)++                Bool                      k v  ->+                    ("Bool",                      k, toJSON v)++                BinaryEquals              k v  ->+                    ("BinaryEquals",              k,+                        toJSON (Text.decodeUtf8 (Base64.encode v)))++                IpAddress                 k v  ->+                    ("IpAddress",                 k,+                        toJSON (Text.decodeUtf8 v))++                NotIpAddress              k v  ->+                    ("NotIpAddress",              k,+                        toJSON (Text.decodeUtf8 v))++                ArnEquals                 k v  ->+                    ("ArnEquals",                 k, toJSON v)+                ArnLike                   k v  ->+                    ("ArnLike",                   k, toJSON v)+                ArnNotEquals              k v  ->+                    ("ArnNotEquals",              k, toJSON v)+                ArnNotLike                k v  ->+                    ("ArnNotLike",                k, toJSON v)+    {-# INLINEABLE toJSON #-}++instance FromJSON Condition where+    parseJSON = JSON.withObject "Condition" $ \o ->+        let parse :: FromJSON a => Text -> JSON.Parser (Key, a)+            parse op = do+                vs <- o .: op+                case toList (vs :: JSON.Object) of+                    [(k, v)] -> (Key k,) <$> JSON.parseJSON v+                    _        ->+                        fail ("Expected one Condition key in " ++ show vs)++            parseNumber op = do+                (k, x) <- parse op+                case Read.readP_to_S scientificP x of+                    [(n, "")] -> pure (k, n)+                    _         ->+                        fail ("Unable to parse numeric Condition from: " ++ show x)++            parseISO8601 op = do+                (k, x) <- parse op+                let fmt    = Time.iso8601DateFormat (Just "%H:%M:%S")+                    locale = Time.defaultTimeLocale+                case Read.readP_to_S (Time.readPTime False locale fmt) x of+                    [(t, "")] -> pure (k, t)+                    _         ->+                        fail ("Unable to parse date Condition from: " ++ show x)++            parsePOSIX op = do+                (k, x) <- parseNumber op+                pure (k, POSIX.posixSecondsToUTCTime (realToFrac x))++            parseDate op =+                parseISO8601 op <|> parsePOSIX op++            parseBool op = do+                (k, x) <- parse op+                case x :: Text of+                    "true"  -> pure (k, True)+                    "false" -> pure (k, False)+                    _       ->+                        fail ("Unable to parse boolean Condition from: " ++ show x)++            parseBinary =+                fmap (second Text.encodeUtf8) . parse++            parseBase64 =+                fmap (second Base64.decodeLenient) . parseBinary++         in uncurry StringEquals+              <$> parse "StringEquals"+        <|> uncurry StringNotEquals+              <$> parse "StringNotEquals"+        <|> uncurry StringEqualsIgnoreCase+              <$> parse "StringEqualsIgnoreCase"+        <|> uncurry StringNotEqualsIgnoreCase+              <$> parse "StringNotEqualsIgnoreCase"+        <|> uncurry StringLike+              <$> parse "StringLike"++        <|> uncurry StringNotLike+              <$> parse "StringNotLike"++        <|> uncurry NumericEquals+             <$> parseNumber "NumericEquals"+        <|> uncurry NumericNotEquals+             <$> parseNumber "NumericNotEquals"+        <|> uncurry NumericLessThan+              <$> parseNumber "NumericLessThan"+        <|> uncurry NumericLessThanEquals+              <$> parseNumber "NumericLessThanEquals"+        <|> uncurry NumericGreaterThan+              <$> parseNumber "NumericGreaterThan"+        <|> uncurry NumericGreaterThanEquals+              <$> parseNumber "NumericGreaterThanEquals"++        <|> uncurry DateEquals+              <$> parseDate "DateEquals"+        <|> uncurry DateNotEquals+              <$> parseDate "DateNotEquals"+        <|> uncurry DateLessThan+              <$> parseDate "DateLessThan"+        <|> uncurry DateLessThanEquals+              <$> parseDate "DateLessThanEquals"+        <|> uncurry DateGreaterThan+              <$> parseDate "DateGreaterThan"+        <|> uncurry DateGreaterThanEquals+              <$> parseDate "DateGreaterThanEquals"++        <|> uncurry Bool+              <$> parseBool "Bool"++        <|> uncurry BinaryEquals+              <$> parseBase64 "BinaryEquals"++        <|> uncurry IpAddress+              <$> parseBinary "IpAddress"+        <|> uncurry NotIpAddress+              <$> parseBinary "NotIpAddress"++        <|> uncurry ArnEquals+              <$> parse "ArnEquals"+        <|> uncurry ArnLike+              <$> parse "ArnLike"+        <|> uncurry ArnNotEquals+              <$> parse "ArnNotEquals"+        <|> uncurry ArnNotLike+              <$> parse "ArnNotLike"++        <|> fail ("Unrecognized Condition: " ++ show o)+    {-# INLINEABLE parseJSON #-}
+ src/Amazonka/IAM/Policy/Lens.hs view
@@ -0,0 +1,127 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE RankNTypes #-}++-- | This module contains lenses and prisms that can be used with any of the+-- various lens libraries. It's recommended that you import this module+-- qualified to avoid ambiguity with "Amazonka.IAM.Policy":+--+-- @+-- import qualified Amazonka.IAM.Policy.Lens as Lens+-- @+module Amazonka.IAM.Policy.Lens+    (+    -- * Lenses++    -- ** Policy+      version+    , id+    , statement++    -- ** Statement+    , sid+    , effect+    , principal+    , action+    , resource+    , condition++    -- * Prisms++    -- ** Block+    , _Match+    , _Not++    -- ** Match+    , _Some+    , _Wildcard+    ) where++import Prelude hiding (id)++import Data.List.NonEmpty     (NonEmpty)+import Data.Profunctor        (dimap)+import Data.Profunctor.Choice (Choice (right'))++import Amazonka.IAM.Policy (Action, Block (..), Condition, Effect, Id,+                            Match (..), Policy, Principal, Resource, Sid,+                            Statement, Version)++import qualified Amazonka.IAM.Policy as Policy++-- Lenses++type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t++type Lens' s a = Lens s s a a++id :: Lens' (Policy a) (Maybe Id)+id f s = (\a -> s { Policy.id = a }) <$> f (Policy.id s)+{-# INLINEABLE id #-}++version :: Lens' (Policy a) (Maybe Version)+version f s = (\a -> s { Policy.version = a }) <$> f (Policy.version s)+{-# INLINEABLE version #-}++statement :: Lens (Policy a) (Policy b) (NonEmpty a) (NonEmpty b)+statement f s = (\a -> s { Policy.statement = a }) <$> f (Policy.statement s)+{-# INLINEABLE statement #-}++sid :: Lens' Statement (Maybe Sid)+sid f s = (\a -> s { Policy.sid = a }) <$> f (Policy.sid s)+{-# INLINEABLE sid #-}++principal :: Lens' Statement (Maybe (Block Principal))+principal f s = (\a -> s { Policy.principal = a }) <$> f (Policy.principal s)+{-# INLINEABLE principal #-}++effect :: Lens' Statement Effect+effect f s = (\a -> s { Policy.effect = a }) <$> f (Policy.effect s)+{-# INLINEABLE effect #-}++action :: Lens' Statement (Block [Action])+action f s = (\a -> s { Policy.action = a }) <$> f (Policy.action s)+{-# INLINEABLE action #-}++resource :: Lens' Statement (Block [Resource])+resource f s = (\a -> s { Policy.resource = a }) <$> f (Policy.resource s)+{-# INLINEABLE resource #-}++condition :: Lens' Statement (Maybe Condition)+condition f s = (\a -> s { Policy.condition = a }) <$> f (Policy.condition s)+{-# INLINEABLE condition #-}++-- Prisms++type Prism s t a b = forall p f. (Choice p, Applicative f) => p a (f b) -> p s (f t)++prism :: (b -> t) -> (s -> Either t a) -> Prism s t a b+prism bt seta = dimap seta (either pure (fmap bt)) . right'+{-# INLINE prism #-}++_Match :: Prism (Block a) (Block a) (Match a) (Match a)+_Match =+    prism Match $ \case+        Match x -> Right x+        y       -> Left  y+{-# INLINEABLE _Match #-}++_Not :: Prism (Block a) (Block a) (Match a) (Match a)+_Not =+    prism Not $ \case+        Not x -> Right x+        y     -> Left  y+{-# INLINEABLE _Not #-}++_Some :: Prism (Match a) (Match b) a b+_Some =+    prism Some $ \case+        Some   x -> Right x+        Wildcard -> Left  Wildcard+{-# INLINE _Some #-}++_Wildcard :: Prism (Match a) (Match a) () a+_Wildcard =+    prism (const Wildcard) $ \case+        Wildcard -> Right ()+        Some x   -> Left  (Some x)+{-# INLINE _Wildcard #-}
+ test/Doctest.hs view
@@ -0,0 +1,6 @@+module Main (main) where++import Test.DocTest (doctest)++main :: IO ()+main = doctest ["-isrc", "--fast", "src/Amazonka/IAM/Policy.hs"]
+ test/Golden.hs view
@@ -0,0 +1,217 @@+{-# LANGUAGE OverloadedLists   #-}+{-# LANGUAGE OverloadedStrings #-}++module Main (main) where++import Amazonka.IAM.Policy (Policy, Statement)++import Data.Aeson     (FromJSON)+import Data.Semigroup ((<>))++import qualified Amazonka.IAM.Policy       as Policy+import qualified Data.Aeson                as JSON+import qualified Data.ByteString.Lazy      as LBS+import qualified Paths_amazonka_iam_policy as Path+import qualified System.IO.Error           as IO+import qualified Test.Hspec                as Hspec++main :: IO ()+main = Hspec.hspec $ do+    test "iam-policy-simulator.json" $+        Policy.singleton+            (Policy.allow+                { Policy.action =+                    Policy.some+                        [ "iam:GetGroup"+                        , "iam:GetGroupPolicy"+                        , "iam:GetPolicy"+                        , "iam:GetPolicyVersion"+                        , "iam:GetRole"+                        , "iam:GetRolePolicy"+                        , "iam:GetUser"+                        , "iam:GetUserPolicy"+                        , "iam:ListAttachedGroupPolicies"+                        , "iam:ListAttachedRolePolicies"+                        , "iam:ListAttachedUserPolicies"+                        , "iam:ListGroups"+                        , "iam:ListGroupPolicies"+                        , "iam:ListGroupsForUser"+                        , "iam:ListRolePolicies"+                        , "iam:ListRoles"+                        , "iam:ListUserPolicies"+                        , "iam:ListUsers"+                        ]+                , Policy.resource = Policy.any+                })++    test "iam-policy-simulator.json" $+        Policy.singleton+            (Policy.allow+                { Policy.action =+                    Policy.some+                        [ "iam:GetGroup"+                        , "iam:GetGroupPolicy"+                        , "iam:GetPolicy"+                        , "iam:GetPolicyVersion"+                        , "iam:GetRole"+                        , "iam:GetRolePolicy"+                        , "iam:GetUser"+                        , "iam:GetUserPolicy"+                        , "iam:ListAttachedGroupPolicies"+                        , "iam:ListAttachedRolePolicies"+                        , "iam:ListAttachedUserPolicies"+                        , "iam:ListGroups"+                        , "iam:ListGroupPolicies"+                        , "iam:ListGroupsForUser"+                        , "iam:ListRolePolicies"+                        , "iam:ListRoles"+                        , "iam:ListUserPolicies"+                        , "iam:ListUsers"+                        ]+                , Policy.resource = Policy.any+                })++    test "iam-user-rotate-credentials.json" $+          Policy.singleton+              (Policy.allow+                  { Policy.action   =+                      Policy.some+                          [ "iam:ListUsers"+                          , "iam:GetAccountPasswordPolicy"+                          ]+                  , Policy.resource = Policy.any+                  })++        <> Policy.singleton+              (Policy.allow+                  { Policy.action   =+                      Policy.some+                          [ "iam:*AccessKey*"+                          , "iam:ChangePassword"+                          , "iam:GetUser"+                          , "iam:*ServiceSpecificCredential*"+                          , "iam:*SigningCertificate*"+                          ]+                  , Policy.resource =+                      Policy.some+                          [ "arn:aws:iam::*:user/${aws:username}"+                          ]+                  })++    test "policy-simulator-api.json" $+        Policy.singleton+            (Policy.allow+                { Policy.action   =+                    Policy.some+                        [ "iam:GetContextKeysForCustomPolicy"+                        , "iam:GetContextKeysForPrincipalPolicy"+                        , "iam:SimulateCustomPolicy"+                        , "iam:SimulatePrincipalPolicy"+                        ]+                , Policy.resource = Policy.any+                })++    test "policy-simulator-console.json" $+           Policy.singleton+               (Policy.allow+                   { Policy.action  =+                       Policy.some+                           [ "iam:GetPolicy"+                           , "iam:GetUserPolicy"+                           ]+                   , Policy.resource = Policy.any+                   })++        <> Policy.singleton+               (Policy.allow+                   { Policy.action   =+                       Policy.some+                           [ "iam:GetUser"+                           , "iam:ListAttachedUserPolicies"+                           , "iam:ListGroupsForUser"+                           , "iam:ListUserPolicies"+                           , "iam:ListUsers"+                           ]+                   , Policy.resource =+                       Policy.some+                           [ "arn:aws:iam::<ACCOUNTNUMBER>:user/<USER-PATH-NAME>/*"+                           ]+                   })++    test "rds-region-admin.json" $+           Policy.singleton+               (Policy.allow+                   { Policy.action   = Policy.some ["rds:*"]+                   , Policy.resource =+                       Policy.some+                           [ "arn:aws:rds:<REGION>:<ACCOUNTNUMBER>:*"+                           ]+                   })++        <> Policy.singleton+               (Policy.allow+                   { Policy.action   = Policy.some ["rds:Describe*"]+                   , Policy.resource = Policy.any+                   })++    test "self-managed-mfa.json" $+           Policy.singleton+               (Policy.allow+                   { Policy.action =+                       Policy.some+                           [ "iam:CreateVirtualMFADevice"+                           , "iam:EnableMFADevice"+                           , "iam:ResyncMFADevice"+                           , "iam:DeleteVirtualMFADevice"+                           ]+                   , Policy.resource =+                       Policy.some+                           [ "arn:aws:iam::*:mfa/${aws:username}"+                           , "arn:aws:iam::*:user/${aws:username}"+                           ]+                   })++        <> Policy.singleton+               (Policy.allow+                   { Policy.sid       =+                       Just "AllowUsersToDeactivateTheirOwnVirtualMFADevice"+                   , Policy.action    =+                       Policy.some+                           [ "iam:DeactivateMFADevice"+                           ]+                   , Policy.resource  =+                       Policy.some+                           [ "arn:aws:iam::*:mfa/${aws:username}"+                           , "arn:aws:iam::*:user/${aws:username}"+                           ]+                   , Policy.condition =+                       Just $ Policy.Bool "aws:MultiFactorAuthPresent" True+                   })++        <> Policy.singleton+               (Policy.allow+                   { Policy.action   =+                       Policy.some+                           [ "iam:ListMFADevices"+                           , "iam:ListVirtualMFADevices"+                           , "iam:ListUsers"+                           ]+                   , Policy.resource = Policy.any+                   })++test :: String -> Policy Statement -> Hspec.Spec+test name actual =+    Hspec.describe name $+        Hspec.it "should equal the serialized haskell value" $+            parseFile ("test/golden/" ++ name)+                >>= Hspec.shouldBe actual++parseFile :: FromJSON a => String -> IO a+parseFile name = do+    path <- Path.getDataFileName name+    lbs  <- LBS.readFile path+    case JSON.eitherDecode' lbs of+        Right x -> pure x+        Left  e ->+            IO.ioError $+                IO.userError ("Failed parsing " ++ path ++ ": " ++ e)
+ test/golden/iam-policy-simulator.json view
@@ -0,0 +1,29 @@+{+  "Version": "2012-10-17",+  "Statement": [+    {+      "Action": [+                "iam:GetGroup",+                "iam:GetGroupPolicy",+                "iam:GetPolicy",+                "iam:GetPolicyVersion",+                "iam:GetRole",+                "iam:GetRolePolicy",+                "iam:GetUser",+                "iam:GetUserPolicy",+                "iam:ListAttachedGroupPolicies",+                "iam:ListAttachedRolePolicies",+                "iam:ListAttachedUserPolicies",+                "iam:ListGroups",+                "iam:ListGroupPolicies",+                "iam:ListGroupsForUser",+                "iam:ListRolePolicies",+                "iam:ListRoles",+                "iam:ListUserPolicies",+                "iam:ListUsers"+       ],+      "Effect": "Allow",+      "Resource":"*"+    }+  ]+}
+ test/golden/iam-user-rotate-credentials.json view
@@ -0,0 +1,24 @@+{+    "Version": "2012-10-17",+    "Statement": [+        {+            "Effect": "Allow",+            "Action": [+                "iam:ListUsers",+                "iam:GetAccountPasswordPolicy"+            ],+            "Resource": "*"+        },+        {+            "Effect": "Allow",+            "Action": [+                "iam:*AccessKey*",+                "iam:ChangePassword",+                "iam:GetUser",+                "iam:*ServiceSpecificCredential*",+                "iam:*SigningCertificate*"+            ],+            "Resource": ["arn:aws:iam::*:user/${aws:username}"]+        }+    ]+}
+ test/golden/policy-simulator-api.json view
@@ -0,0 +1,15 @@+{+  "Version" : "2012-10-17",+  "Statement" : [+    {+      "Action" : [+        "iam:GetContextKeysForCustomPolicy",+        "iam:GetContextKeysForPrincipalPolicy",+        "iam:SimulateCustomPolicy",+        "iam:SimulatePrincipalPolicy"+      ],+      "Effect" : "Allow",+      "Resource" : ["*"]+    }+  ]+}
+ test/golden/policy-simulator-console.json view
@@ -0,0 +1,24 @@+{+  "Version": "2012-10-17",+  "Statement": [+    {+      "Action": [+        "iam:GetPolicy",+        "iam:GetUserPolicy"+      ],+      "Effect": "Allow",+      "Resource": "*"+    },+    {+      "Action": [+        "iam:GetUser",+        "iam:ListAttachedUserPolicies",+        "iam:ListGroupsForUser",+        "iam:ListUserPolicies",+        "iam:ListUsers"+      ],+      "Effect": "Allow",+      "Resource": "arn:aws:iam::<ACCOUNTNUMBER>:user/<USER-PATH-NAME>/*"+    }+  ]+}
+ test/golden/rds-region-admin.json view
@@ -0,0 +1,21 @@+{+  "Version": "2012-10-17",+  "Statement": [+      {+          "Effect": "Allow",+          "Action": "rds:*",+          "Resource": [+              "arn:aws:rds:<REGION>:<ACCOUNTNUMBER>:*"+          ]+      },+      {+          "Effect": "Allow",+          "Action": [+              "rds:Describe*"+          ],+          "Resource": [+              "*"+          ]+      }+  ]+}
+ test/golden/rds-tagged-resources.json view
@@ -0,0 +1,108 @@+{+  "Version": "2012-10-17",+  "Statement": [+      {+          "Action": [+              "rds:Describe*",+              "rds:List*"+          ],+          "Effect": "Allow",+          "Resource": "*"+      },+      {+          "Action": [+              "rds:DeleteDBInstance",+              "rds:RebootDBInstance",+              "rds:ModifyDBInstance"+          ],+          "Effect": "Allow",+          "Resource": "*",+          "Condition": {+              "StringEqualsIgnoreCase": {+                  "rds:db-tag/Owner": "${aws:username}"+              }+          }+      },+      {+          "Action": [+              "rds:ModifyOptionGroup",+              "rds:DeleteOptionGroup"+          ],+          "Effect": "Allow",+          "Resource": "*",+          "Condition": {+              "StringEqualsIgnoreCase": {+                  "rds:og-tag/Owner": "${aws:username}"+              }+          }+      },+      {+          "Action": [+              "rds:ModifyDBParameterGroup",+              "rds:ResetDBParameterGroup"+          ],+          "Effect": "Allow",+          "Resource": "*",+          "Condition": {+              "StringEqualsIgnoreCase": {+                  "rds:pg-tag/Owner": "${aws:username}"+              }+          }+      },+      {+          "Action": [+              "rds:AuthorizeDBSecurityGroupIngress",+              "rds:RevokeDBSecurityGroupIngress",+              "rds:DeleteDBSecurityGroup"+          ],+          "Effect": "Allow",+          "Resource": "*",+          "Condition": {+              "StringEqualsIgnoreCase": {+                  "rds:secgrp-tag/Owner": "${aws:username}"+              }+          }+      },+      {+          "Action": [+              "rds:DeleteDBSnapshot",+              "rds:RestoreDBInstanceFromDBSnapshot"+          ],+          "Effect": "Allow",+          "Resource": "*",+          "Condition": {+              "StringEqualsIgnoreCase": {+                  "rds:snapshot-tag/Owner": "${aws:username}"+              }+          }+      },+      {+          "Action": [+              "rds:ModifyDBSubnetGroup",+              "rds:DeleteDBSubnetGroup"+          ],+          "Effect": "Allow",+          "Resource": "*",+          "Condition": {+              "StringEqualsIgnoreCase": {+                  "rds:subgrp-tag/Owner": "${aws:username}"+              }+          }+      },+      {+          "Action": [+              "rds:ModifyEventSubscription",+              "rds:AddSourceIdentifierToSubscription",+              "rds:RemoveSourceIdentifierFromSubscription",+              "rds:DeleteEventSubscription"+          ],+          "Effect": "Allow",+          "Resource": "*",+          "Condition": {+              "StringEqualsIgnoreCase": {+                  "rds:es-tag/Owner": "${aws:username}"+              }+          }+      }+  ]+}
+ test/golden/s3-bucket-management.json view
@@ -0,0 +1,21 @@+{+  "Version": "2012-10-17",+  "Statement": [+    {+      "Effect": "Allow",+      "Action": "s3:*",+      "Resource": [+        "arn:aws:s3:::<BUCKET-NAME>",+        "arn:aws:s3:::<BUCKET-NAME>/*"+      ]+    },+    {+      "Effect": "Deny",+      "NotAction": "s3:*",+      "NotResource": [+        "arn:aws:s3:::<BUCKET-NAME>",+        "arn:aws:s3:::<BUCKET-NAME>/*"+      ]+    }+  ]+}
+ test/golden/s3-bucket-read-write.json view
@@ -0,0 +1,18 @@+{+   "Version": "2012-10-17",+   "Statement": [+     {+       "Effect": "Allow",+       "Action": ["s3:ListBucket"],+       "Resource": ["arn:aws:s3:::<BUCKET-NAME>"]+     },+     {+       "Effect": "Allow",+       "Action": [+         "s3:PutObject",+         "s3:GetObject"+       ],+       "Resource": ["arn:aws:s3:::<BUCKET-NAME>/*"]+     }+   ]+ }
+ test/golden/s3-cognito-bucket-objects.json view
@@ -0,0 +1,23 @@+{+  "Version": "2012-10-17",+  "Statement": [+    {+      "Effect": "Allow",+      "Action": ["s3:ListBucket"],+      "Resource": ["arn:aws:s3:::<BUCKET-NAME>"],+      "Condition": {"StringLike": {"s3:prefix": ["cognito/<APPLICATION-NAME>/"]}}+    },+    {+      "Effect": "Allow",+      "Action": [+        "s3:GetObject",+        "s3:PutObject",+        "s3:DeleteObject"+      ],+      "Resource": [+        "arn:aws:s3:::<BUCKET-NAME>/cognito/<APPLICATION-NAME>/${cognito-identity.amazonaws.com:sub}",+        "arn:aws:s3:::<BUCKET-NAME>/cognito/<APPLICATION-NAME>/${cognito-identity.amazonaws.com:sub}/*"+      ]+    }+  ]+}
+ test/golden/s3-iam-user-home-directory.json view
@@ -0,0 +1,31 @@+{+  "Version": "2012-10-17",+  "Statement": [+    {+      "Effect": "Allow",+      "Action": [+        "s3:ListAllMyBuckets",+        "s3:GetBucketLocation"+      ],+      "Resource": "arn:aws:s3:::*"+    },+    {+      "Effect": "Allow",+      "Action": "s3:ListBucket",+      "Resource": "arn:aws:s3:::<BUCKET-NAME>",+      "Condition": {"StringLike": {"s3:prefix": [+        "",+        "home/",+        "home/${aws:username}/*"+      ]}}+    },+    {+      "Effect": "Allow",+      "Action": "s3:*",+      "Resource": [+        "arn:aws:s3:::<BUCKET-NAME>/home/${aws:username}",+        "arn:aws:s3:::<BUCKET-NAME>/home/${aws:username}/*"+      ]+    }+  ]+}
+ test/golden/self-managed-mfa.json view
@@ -0,0 +1,43 @@+{+  "Version": "2012-10-17",+  "Statement": [+    {+      "Effect": "Allow",+      "Action": [+        "iam:CreateVirtualMFADevice",+        "iam:EnableMFADevice",+        "iam:ResyncMFADevice",+        "iam:DeleteVirtualMFADevice"+      ],+      "Resource": [+        "arn:aws:iam::*:mfa/${aws:username}",+        "arn:aws:iam::*:user/${aws:username}"+      ]+    },+    {+      "Sid": "AllowUsersToDeactivateTheirOwnVirtualMFADevice",+      "Effect": "Allow",+      "Action": [+        "iam:DeactivateMFADevice"+      ],+      "Resource": [+        "arn:aws:iam::*:mfa/${aws:username}",+        "arn:aws:iam::*:user/${aws:username}"+      ],+      "Condition": {+        "Bool": {+          "aws:MultiFactorAuthPresent": "true"+        }+      }+    },+    {+      "Effect": "Allow",+      "Action": [+        "iam:ListMFADevices",+        "iam:ListVirtualMFADevices",+        "iam:ListUsers"+      ],+      "Resource": "*"+    }+  ]+}