From f85ec8baa2f9aaeead4f619dccfa3d38880a16d7 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Tue, 26 Sep 2023 23:05:25 +0200 Subject: [PATCH] feat: Add grant ownership to SDK (#2064) * Update README * Add grant ownership to interface * Add grant ownership implementation and validations * Add validation tests * Add missing tests * Add integration tests * Update go.mod * Change to enum * Add test for warehouse * Reorganise grant ownership for all kinds of objects * Trigger new pipeline --- README.md | 4 +- go.mod | 1 - go.sum | 50 ++---------- pkg/sdk/grants.go | 36 ++++++++ pkg/sdk/grants_impl.go | 9 ++ pkg/sdk/grants_integration_test.go | 127 ++++++++++++++++++++++++++++- pkg/sdk/grants_test.go | 121 +++++++++++++++++++++++++++ pkg/sdk/grants_validations.go | 35 ++++++++ pkg/sdk/privileges.go | 2 + 9 files changed, 336 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index ffa68141b9..37186bdc03 100644 --- a/README.md +++ b/README.md @@ -69,13 +69,13 @@ Integration status - indicates if given resource / datasource is using new SDK. | Database Role | ✅ | snowflake_database_role | snowflake_database_role | ✅ | | Role | 👨‍💻 | snowflake_role | snowflake_role | 👨‍💻 | | Grant Privilege to Application Role | ❌ | snowflake_grant_privileges_to_application_role | snowflake_grants | ❌ | -| Grant Privilege to Database Role | 👨‍💻 | snowflake_grant_privileges_to_database_role | snowflake_grants | 👨‍💻 | +| Grant Privilege to Database Role | ✅ | snowflake_grant_privileges_to_database_role | snowflake_grants | 👨‍💻 | | Grant Privilege to Role | ❌ | snowflake_grant_privileges_to_role | snowflake_grants | ✅ | | Grant Role | ❌ | snowflake_grant_role | snowflake_grants | ❌ | | Grant Database Role | ✅ | snowflake_grant_database_role | snowflake_grants | ❌ | | Grant Application Role | ❌ | snowflake_grant_application_role | snowflake_grants | ❌ | | Grant Privilege to Share | ✅ | snowflake_grant_privileges_to_share | snowflake_grants | ❌ | -| Grant Ownership | ❌ | snowflake_grant_ownership | snowflake_grants | ❌ | +| Grant Ownership | ✅ | snowflake_grant_ownership | snowflake_grants | ❌ | | API Integration | ❌ | snowflake_api_integration | snowflake_integrations | ❌ | | Notification Integration | ❌ | snowflake_notification_integration | snowflake_integrations | ❌ | | Security Integration | ❌ | snowflake_security_integration | snowflake_integrations | ❌ | diff --git a/go.mod b/go.mod index 42d2a2257a..79e5996b92 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,6 @@ require ( github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/andybalholm/brotli v1.0.4 // indirect - github.com/apache/arrow/go/v10 v10.0.1 // indirect github.com/apache/arrow/go/v12 v12.0.1 // indirect github.com/apache/thrift v0.16.0 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect diff --git a/go.sum b/go.sum index 969a16250d..efe0d2ab1c 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,6 @@ github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/apache/arrow/go/v10 v10.0.1 h1:n9dERvixoC/1JjDmBcs9FPaEryoANa2sCgVFo6ez9cI= -github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= github.com/apache/arrow/go/v12 v12.0.1 h1:JsR2+hzYYjgSUkBSaahpqCetqZMr76djX80fF/DiJbg= github.com/apache/arrow/go/v12 v12.0.1/go.mod h1:weuTY7JvTG/HDPtMQxEUp7pU73vkLWMLpY67QwZ/WWw= github.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY= @@ -51,67 +49,41 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= -github.com/aws/aws-sdk-go-v2 v1.17.6 h1:Y773UK7OBqhzi5VDXMi1zVGsoj+CVHs2eaC2bDsLwi0= -github.com/aws/aws-sdk-go-v2 v1.17.6/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2 v1.17.7 h1:CLSjnhJSTSogvqUGhIC6LqFKATMRexcxLZ0i/Nzk9Eg= github.com/aws/aws-sdk-go-v2 v1.17.7/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno= -github.com/aws/aws-sdk-go-v2/config v1.18.17 h1:jwTkhULSrbr/SQA8tfdYqZxpG8YsRycmIXxJcbrqY5E= -github.com/aws/aws-sdk-go-v2/config v1.18.17/go.mod h1:Lj3E7XcxJnxMa+AYo89YiL68s1cFJRGduChynYU67VA= +github.com/aws/aws-sdk-go-v2/config v1.18.19 h1:AqFK6zFNtq4i1EYu+eC7lcKHYnZagMn6SW171la0bGw= github.com/aws/aws-sdk-go-v2/config v1.18.19/go.mod h1:XvTmGMY8d52ougvakOv1RpiTLPz9dlG/OQHsKU/cMmY= -github.com/aws/aws-sdk-go-v2/credentials v1.13.17 h1:IubQO/RNeIVKF5Jy77w/LfUvmmCxTnk2TP1UZZIMiF4= -github.com/aws/aws-sdk-go-v2/credentials v1.13.17/go.mod h1:K9xeFo1g/YPMguMUD69YpwB4Nyi6W/5wn706xIInJFg= github.com/aws/aws-sdk-go-v2/credentials v1.13.18 h1:EQMdtHwz0ILTW1hoP+EwuWhwCG1hD6l3+RWFQABET4c= github.com/aws/aws-sdk-go-v2/credentials v1.13.18/go.mod h1:vnwlwjIe+3XJPBYKu1et30ZPABG3VaXJYr8ryohpIyM= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.0 h1:/2Cb3SK3xVOQA7Xfr5nCWCo5H3UiNINtsVvVdk8sQqA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.0/go.mod h1:neYVaeKr5eT7BzwULuG2YbLhzWZ22lpjKdCybR7AXrQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1 h1:gt57MN3liKiyGopcqgNzJb2+d9MJaKT/q1OksHNXVE4= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1/go.mod h1:lfUx8puBRdM5lVVMQlwt2v+ofiG/X6Ms+dy0UkG/kXw= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.57 h1:ubKS0iZH5veiqb44qeHzaoKNPvCZQeBVFw4JDhfeWjk= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.57/go.mod h1:dRBjXtcjmYglxVHpdoGGVWvZumDC27I2GLDGI0Uw4RQ= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.59 h1:E3Y+OfzOK1+rmRo/K2G0ml8Vs+Xqk0kOnf4nS0kUtBc= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.59/go.mod h1:1M4PLSBUVfBI0aP+C9XI7SM6kZPCGYyI6izWz0TGprE= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.30 h1:y+8n9AGDjikyXoMBTRaHHHSaFEB8267ykmvyPodJfys= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.30/go.mod h1:LUBAO3zNXQjoONBKn/kR1y0Q4cj/D02Ts0uHYjcCQLM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31 h1:sJLYcS+eZn5EeNINGHSCRAwUJMFVqklwkH36Vbyai7M= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31/go.mod h1:QT0BqUvX1Bh2ABdTGnjqEjvjzrCfIniM9Sc8zn9Yndo= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24 h1:r+Kv+SEJquhAZXaJ7G4u44cIwXV3f8K+N482NNAzJZA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24/go.mod h1:gAuCezX/gob6BSMbItsSlMb6WZGV7K2+fWOvk8xBSto= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25 h1:1mnRASEKnkqsntcxHaysxwgVoUUp5dkiB+l3llKnqyg= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25/go.mod h1:zBHOPwhBc3FlQjQJE/D3IfPWiWaQmT06Vq9aNukDo0k= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.31 h1:hf+Vhp5WtTdcSdE+yEcUz8L73sAzN0R+0jQv+Z51/mI= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.31/go.mod h1:5zUjguZfG5qjhG9/wqmuyHRyUftl2B5Cp6NNxNC6kRA= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32 h1:p5luUImdIqywn6JpQsW3tq5GNOxKmOnEpybzPx+d1lk= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32/go.mod h1:XGhIBZDEgfqmFIugclZ6FU7v75nHhBDtzuB4xB/tEi4= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.22 h1:lTqBRUuy8oLhBsnnVZf14uRbIHPHCrGqg4Plc8gU/1U= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.22/go.mod h1:YsOa3tFriwWNvBPYHXM5ARiU2yqBNWPWeUiq+4i7Na0= github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23 h1:DWYZIsyqagnWL00f8M/SOr9fN063OEQWn9LLTbdYXsk= github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23/go.mod h1:uIiFgURZbACBEQJfqTZPb/jxO7R+9LeoHUFudtIdeQI= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.25 h1:B/hO3jfWRm7hP00UeieNlI5O2xP5WJ27tyJG5lzc7AM= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.25/go.mod h1:54K1zgxK/lai3a4HosE4IKBwZsP/5YAJ6dzJfwsjJ0U= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26 h1:CeuSeq/8FnYpPtnuIeLQEEvDv9zUjneuYi8EghMBdwQ= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26/go.mod h1:2UqAAwMUXKeRkAHIlDJqvMVgOWkUi/AUXPk/YIe+Dg4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.24 h1:c5qGfdbCHav6viBwiyDns3OXqhqAbGjfIB4uVu2ayhk= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.24/go.mod h1:HMA4FZG6fyib+NDo5bpIxX1EhYjrAOveZJY2YR0xrNE= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25 h1:5LHn8JQ0qvjD9L9JhMtylnkcw7j05GDZqM9Oin6hpr0= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25/go.mod h1:/95IA+0lMnzW6XzqYJRpjjsAbKEORVeO0anQqjd2CNU= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.24 h1:i4RH8DLv/BHY0fCrXYQDr+DGnWzaxB3Ee/esxUaSavk= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.24/go.mod h1:N8X45/o2cngvjCYi2ZnvI0P4mU4ZRJfEYC3maCSsPyw= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0 h1:e2ooMhpYGhDnBfSvIyusvAwX7KexuZaHbQY2Dyei7VU= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0/go.mod h1:bh2E0CXKZsQN+faiKVqC40vfNMAWheoULBCnEgO9K+8= -github.com/aws/aws-sdk-go-v2/service/s3 v1.30.6 h1:zzTm99krKsFcF4N7pu2z17yCcAZpQYZ7jnJZPIgEMXE= -github.com/aws/aws-sdk-go-v2/service/s3 v1.30.6/go.mod h1:PudwVKUTApfm0nYaPutOXaKdPKTlZYClGBQpVIRdcbs= github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0 h1:B1G2pSPvbAtQjilPq+Y7jLIzCOwKzuVEl+aBBaNG0AQ= github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0/go.mod h1:ncltU6n4Nof5uJttDtcNQ537uNuwYqsZZQcpkd2/GUQ= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.5 h1:bdKIX6SVF3nc3xJFw6Nf0igzS6Ff/louGq8Z6VP/3Hs= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.5/go.mod h1:vuWiaDB30M/QTC+lI3Wj6S/zb7tpUK2MSYgy3Guh2L0= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.6 h1:5V7DWLBd7wTELVz5bPpwzYy/sikk0gsgZfj40X+l5OI= github.com/aws/aws-sdk-go-v2/service/sso v1.12.6/go.mod h1:Y1VOmit/Fn6Tz1uFAeCO6Q7M2fmfXSCLeL5INVYsLuY= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.5 h1:xLPZMyuZ4GuqRCIec/zWuIhRFPXh2UOJdLXBSi64ZWQ= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.5/go.mod h1:QjxpHmCwAg0ESGtPQnLIVp7SedTOBMYy+Slr3IfMKeI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6 h1:B8cauxOH1W1v7rd8RdI/MWnoR4Ze0wIHWrb90qczxj4= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6/go.mod h1:Lh/bc9XUf8CfOY6Jp5aIkQtN+j1mc+nExc+KXj9jx2s= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.6 h1:rIFn5J3yDoeuKCE9sESXqM5POTAhOP1du3bv/qTL+tE= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.6/go.mod h1:48WJ9l3dwP0GSHWGc5sFGGlCkuA82Mc2xnw+T6Q8aDw= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.7 h1:bWNgNdRko2x6gqa0blfATqAZKZokPIeM1vfmQt2pnvM= github.com/aws/aws-sdk-go-v2/service/sts v1.18.7/go.mod h1:JuTnSoeePXmMVe9G8NcjjwgOKEfZ4cOjMuT2IBT/2eI= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= @@ -154,8 +126,6 @@ github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= -github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= -github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= @@ -259,14 +229,12 @@ github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -332,7 +300,6 @@ github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSg github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= -github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -341,8 +308,6 @@ github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFR github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/snowflakedb/gosnowflake v1.6.19 h1:KSHXrQ5o7uso25hNIzi/RObXtnSGkFgie91X82KcvMY= -github.com/snowflakedb/gosnowflake v1.6.19/go.mod h1:FM1+PWUdwB9udFDsXdfD58NONC0m+MlOSmQRvimobSM= github.com/snowflakedb/gosnowflake v1.6.24 h1:NiBh1WSstNtr12qywmdFMS1XHaYdF5iWWGnjIQb1cEY= github.com/snowflakedb/gosnowflake v1.6.24/go.mod h1:KfO4F7bk+aXPUIvBqYxvPhxLlu2/w4TtSC8Rw/yr5Mg= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -380,7 +345,6 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.13.1 h1:0a6bRwuiSHtAmqCqNOE+c2oHgepv0ctoxU4FUe43kwc= github.com/zclconf/go-cty v1.13.1/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= -github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= diff --git a/pkg/sdk/grants.go b/pkg/sdk/grants.go index 7710e315bb..ee50beadd1 100644 --- a/pkg/sdk/grants.go +++ b/pkg/sdk/grants.go @@ -15,6 +15,8 @@ type Grants interface { RevokePrivilegesFromDatabaseRole(ctx context.Context, privileges *DatabaseRoleGrantPrivileges, on *DatabaseRoleGrantOn, role DatabaseObjectIdentifier, opts *RevokePrivilegesFromDatabaseRoleOptions) error GrantPrivilegeToShare(ctx context.Context, privilege ObjectPrivilege, on *GrantPrivilegeToShareOn, to AccountObjectIdentifier) error RevokePrivilegeFromShare(ctx context.Context, privilege ObjectPrivilege, on *RevokePrivilegeFromShareOn, from AccountObjectIdentifier) error + GrantOwnership(ctx context.Context, on OwnershipGrantOn, to OwnershipGrantTo, opts *GrantOwnershipOptions) error + Show(ctx context.Context, opts *ShowGrantOptions) ([]Grant, error) } @@ -250,3 +252,37 @@ func (row grantRow) convert() *Grant { GrantedBy: NewAccountObjectIdentifier(row.GrantedBy), } } + +// GrantOwnershipOptions is based on https://docs.snowflake.com/en/sql-reference/sql/grant-ownership#syntax. +// Description is a bit misleading, ownership can be given not only to schema objects but also to account level objects. +type GrantOwnershipOptions struct { + grantOwnership bool `ddl:"static" sql:"GRANT OWNERSHIP"` + On OwnershipGrantOn `ddl:"keyword" sql:"ON"` + To OwnershipGrantTo `ddl:"keyword" sql:"TO"` + CurrentGrants *OwnershipCurrentGrants `ddl:"-"` +} + +type OwnershipGrantOn struct { + // One of + Object *Object `ddl:"-"` + All *GrantOnSchemaObjectIn `ddl:"keyword" sql:"ALL"` + Future *GrantOnSchemaObjectIn `ddl:"keyword" sql:"FUTURE"` +} + +type OwnershipGrantTo struct { + // One of + DatabaseRoleName *DatabaseObjectIdentifier `ddl:"identifier" sql:"DATABASE ROLE"` + AccountRoleName *AccountObjectIdentifier `ddl:"identifier" sql:"ROLE"` +} + +type OwnershipCurrentGrants struct { + OutboundPrivileges OwnershipCurrentGrantsOutboundPrivileges `ddl:"keyword"` + currentGrants bool `ddl:"static" sql:"CURRENT GRANTS"` +} + +type OwnershipCurrentGrantsOutboundPrivileges string + +const ( + Revoke OwnershipCurrentGrantsOutboundPrivileges = "REVOKE" + Copy OwnershipCurrentGrantsOutboundPrivileges = "COPY" +) diff --git a/pkg/sdk/grants_impl.go b/pkg/sdk/grants_impl.go index 3d03a79c3f..e2a078556e 100644 --- a/pkg/sdk/grants_impl.go +++ b/pkg/sdk/grants_impl.go @@ -66,6 +66,15 @@ func (v *grants) RevokePrivilegeFromShare(ctx context.Context, privilege ObjectP return validateAndExec(v.client, ctx, opts) } +func (v *grants) GrantOwnership(ctx context.Context, on OwnershipGrantOn, to OwnershipGrantTo, opts *GrantOwnershipOptions) error { + if opts == nil { + opts = &GrantOwnershipOptions{} + } + opts.On = on + opts.To = to + return validateAndExec(v.client, ctx, opts) +} + func (v *grants) Show(ctx context.Context, opts *ShowGrantOptions) ([]Grant, error) { if opts == nil { opts = &ShowGrantOptions{} diff --git a/pkg/sdk/grants_integration_test.go b/pkg/sdk/grants_integration_test.go index 90b74837fb..fc9ad88b74 100644 --- a/pkg/sdk/grants_integration_test.go +++ b/pkg/sdk/grants_integration_test.go @@ -243,7 +243,7 @@ func TestInt_GrantAndRevokePrivilegesToDatabaseRole(t *testing.T) { }, }) require.NoError(t, err) - // Expecting two grants because database rol has usage on database by default + // Expecting two grants because database role has usage on database by default require.Equal(t, 2, len(returnedGrants)) usagePrivilege, err := findOne[Grant](returnedGrants, func(g Grant) bool { return g.Privilege == AccountObjectPrivilegeUsage.String() }) @@ -292,7 +292,7 @@ func TestInt_GrantAndRevokePrivilegesToDatabaseRole(t *testing.T) { }, }) require.NoError(t, err) - // Expecting two grants because database rol has usage on database by default + // Expecting two grants because database role has usage on database by default require.Equal(t, 2, len(returnedGrants)) usagePrivilege, err := findOne[Grant](returnedGrants, func(g Grant) bool { return g.Privilege == AccountObjectPrivilegeUsage.String() }) @@ -345,7 +345,7 @@ func TestInt_GrantAndRevokePrivilegesToDatabaseRole(t *testing.T) { }, }) require.NoError(t, err) - // Expecting two grants because database rol has usage on database by default + // Expecting two grants because database role has usage on database by default require.Equal(t, 2, len(returnedGrants)) usagePrivilege, err := findOne[Grant](returnedGrants, func(g Grant) bool { return g.Privilege == AccountObjectPrivilegeUsage.String() }) @@ -486,6 +486,127 @@ func TestInt_RevokePrivilegeToShare(t *testing.T) { }) } +func TestInt_GrantOwnership(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + database, databaseCleanup := createDatabase(t, client) + t.Cleanup(databaseCleanup) + + t.Run("on schema object to database role", func(t *testing.T) { + databaseRole, _ := createDatabaseRole(t, client, database) + databaseRoleId := NewDatabaseObjectIdentifier(database.Name, databaseRole.Name) + schema, _ := createSchema(t, client, database) + table, _ := createTable(t, client, database, schema) + + on := OwnershipGrantOn{ + Object: &Object{ + ObjectType: ObjectTypeTable, + Name: table.ID(), + }, + } + to := OwnershipGrantTo{ + DatabaseRoleName: &databaseRoleId, + } + + err := client.Grants.GrantOwnership(ctx, on, to, nil) + require.NoError(t, err) + + returnedGrants, err := client.Grants.Show(ctx, &ShowGrantOptions{ + To: &ShowGrantsTo{ + DatabaseRole: databaseRoleId, + }, + }) + require.NoError(t, err) + // Expecting two grants because database role has usage on database by default + require.Equal(t, 2, len(returnedGrants)) + + usagePrivilege, err := findOne[Grant](returnedGrants, func(g Grant) bool { return g.Privilege == AccountObjectPrivilegeUsage.String() }) + require.NoError(t, err) + assert.Equal(t, ObjectTypeDatabaseRole, usagePrivilege.GrantedTo) + + ownership, err := findOne[Grant](returnedGrants, func(g Grant) bool { return g.Privilege == SchemaObjectOwnership.String() }) + require.NoError(t, err) + assert.Equal(t, ObjectTypeTable, ownership.GrantedOn) + assert.Equal(t, ObjectTypeDatabaseRole, ownership.GrantedTo) + assert.Equal(t, table.ID().FullyQualifiedName(), ownership.Name.FullyQualifiedName()) + }) + + t.Run("on future schema object in database to role", func(t *testing.T) { + role, roleCleanup := createRole(t, client) + t.Cleanup(roleCleanup) + roleId := role.ID() + + on := OwnershipGrantOn{ + Future: &GrantOnSchemaObjectIn{ + PluralObjectType: PluralObjectTypeExternalTables, + InDatabase: Pointer(database.ID()), + }, + } + to := OwnershipGrantTo{ + AccountRoleName: &roleId, + } + + err := client.Grants.GrantOwnership(ctx, on, to, nil) + require.NoError(t, err) + + returnedGrants, err := client.Grants.Show(ctx, &ShowGrantOptions{ + Future: Bool(true), + To: &ShowGrantsTo{ + Role: roleId, + }, + }) + require.NoError(t, err) + require.Equal(t, 1, len(returnedGrants)) + + assert.Equal(t, SchemaObjectOwnership.String(), returnedGrants[0].Privilege) + assert.Equal(t, ObjectTypeExternalTable, returnedGrants[0].GrantOn) + assert.Equal(t, ObjectTypeRole, returnedGrants[0].GrantTo) + assert.Equal(t, roleId, returnedGrants[0].GranteeName) + }) + + t.Run("on account level object to role", func(t *testing.T) { + warehouse, warehouseCleanup := createWarehouse(t, client) + t.Cleanup(warehouseCleanup) + + // role is deliberately created after warehouse, so that cleanup is done in reverse + // because after ownership grant we lose privilege to drop object + // with first dropping the role, we reacquire rights to do it - a little hacky trick + role, roleCleanup := createRole(t, client) + t.Cleanup(roleCleanup) + roleId := role.ID() + + on := OwnershipGrantOn{ + Object: &Object{ + ObjectType: ObjectTypeWarehouse, + Name: warehouse.ID(), + }, + } + to := OwnershipGrantTo{ + AccountRoleName: &roleId, + } + currentGrants := OwnershipCurrentGrants{ + OutboundPrivileges: Copy, + } + + err := client.Grants.GrantOwnership(ctx, on, to, &GrantOwnershipOptions{CurrentGrants: ¤tGrants}) + require.NoError(t, err) + + returnedGrants, err := client.Grants.Show(ctx, &ShowGrantOptions{ + To: &ShowGrantsTo{ + Role: roleId, + }, + }) + require.NoError(t, err) + require.Equal(t, 1, len(returnedGrants)) + + assert.Equal(t, SchemaObjectOwnership.String(), returnedGrants[0].Privilege) + assert.Equal(t, ObjectTypeWarehouse, returnedGrants[0].GrantedOn) + assert.Equal(t, ObjectTypeRole, returnedGrants[0].GrantedTo) + assert.Equal(t, roleId, returnedGrants[0].GranteeName) + }) +} + func TestInt_ShowGrants(t *testing.T) { client := testClient(t) ctx := context.Background() diff --git a/pkg/sdk/grants_test.go b/pkg/sdk/grants_test.go index 1c600e708b..733a59d44c 100644 --- a/pkg/sdk/grants_test.go +++ b/pkg/sdk/grants_test.go @@ -804,6 +804,127 @@ func TestRevokePrivilegeFromShare(t *testing.T) { }) } +func TestGrants_GrantOwnership(t *testing.T) { + dbId := NewAccountObjectIdentifier("db1") + schemaId := NewDatabaseObjectIdentifier("db1", "schema1") + roleId := NewAccountObjectIdentifier("role1") + databaseRoleId := NewDatabaseObjectIdentifier("db1", "role1") + tableId := NewSchemaObjectIdentifier("db1", "schema1", "table1") + + defaultOpts := func() *GrantOwnershipOptions { + return &GrantOwnershipOptions{ + On: OwnershipGrantOn{ + Object: &Object{ + ObjectType: ObjectTypeTable, + Name: tableId, + }, + }, + To: OwnershipGrantTo{ + AccountRoleName: Pointer(roleId), + }, + } + } + + t.Run("validation: grant on empty", func(t *testing.T) { + opts := defaultOpts() + opts.On = OwnershipGrantOn{} + assertOptsInvalid(t, opts, fmt.Errorf("exactly one of [Object AllIn Future] must be set")) + }) + + t.Run("validation: grant on too many", func(t *testing.T) { + opts := defaultOpts() + opts.On = OwnershipGrantOn{ + Object: &Object{ + ObjectType: ObjectTypeTable, + Name: tableId, + }, + Future: &GrantOnSchemaObjectIn{ + PluralObjectType: PluralObjectTypeTables, + InDatabase: Pointer(dbId), + }, + } + assertOptsInvalid(t, opts, fmt.Errorf("exactly one of [Object AllIn Future] must be set")) + }) + + t.Run("validation: grant on schema object - all", func(t *testing.T) { + opts := defaultOpts() + opts.On = OwnershipGrantOn{ + All: &GrantOnSchemaObjectIn{ + PluralObjectType: PluralObjectTypeTables, + }, + } + assertOptsInvalid(t, opts, fmt.Errorf("exactly one of InDatabase, or InSchema must be set")) + }) + + t.Run("validation: grant on schema object - future", func(t *testing.T) { + opts := defaultOpts() + opts.On = OwnershipGrantOn{ + Future: &GrantOnSchemaObjectIn{ + PluralObjectType: PluralObjectTypeTables, + }, + } + assertOptsInvalid(t, opts, fmt.Errorf("exactly one of InDatabase, or InSchema must be set")) + }) + + t.Run("validation: grant to empty", func(t *testing.T) { + opts := defaultOpts() + opts.To = OwnershipGrantTo{} + assertOptsInvalid(t, opts, fmt.Errorf("exactly one of [databaseRoleName accountRoleName] must be set")) + }) + + t.Run("validation: grant to role and database role", func(t *testing.T) { + opts := defaultOpts() + opts.To = OwnershipGrantTo{ + DatabaseRoleName: Pointer(databaseRoleId), + AccountRoleName: Pointer(roleId), + } + assertOptsInvalid(t, opts, fmt.Errorf("exactly one of [databaseRoleName accountRoleName] must be set")) + }) + + t.Run("on schema object to role", func(t *testing.T) { + opts := defaultOpts() + assertOptsValidAndSQLEquals(t, opts, `GRANT OWNERSHIP ON TABLE %s TO ROLE %s`, tableId.FullyQualifiedName(), roleId.FullyQualifiedName()) + }) + + t.Run("on schema object to database role", func(t *testing.T) { + opts := defaultOpts() + opts.To = OwnershipGrantTo{ + DatabaseRoleName: Pointer(databaseRoleId), + } + assertOptsValidAndSQLEquals(t, opts, `GRANT OWNERSHIP ON TABLE %s TO DATABASE ROLE %s`, tableId.FullyQualifiedName(), databaseRoleId.FullyQualifiedName()) + }) + + t.Run("on future schema object in database", func(t *testing.T) { + opts := defaultOpts() + opts.On = OwnershipGrantOn{ + Future: &GrantOnSchemaObjectIn{ + PluralObjectType: PluralObjectTypeTables, + InDatabase: Pointer(dbId), + }, + } + assertOptsValidAndSQLEquals(t, opts, `GRANT OWNERSHIP ON FUTURE TABLES IN DATABASE %s TO ROLE %s`, dbId.FullyQualifiedName(), roleId.FullyQualifiedName()) + }) + + t.Run("on all schema objects in schema", func(t *testing.T) { + opts := defaultOpts() + opts.On = OwnershipGrantOn{ + All: &GrantOnSchemaObjectIn{ + PluralObjectType: PluralObjectTypeTables, + InSchema: Pointer(schemaId), + }, + } + assertOptsValidAndSQLEquals(t, opts, `GRANT OWNERSHIP ON ALL TABLES IN SCHEMA %s TO ROLE %s`, schemaId.FullyQualifiedName(), roleId.FullyQualifiedName()) + }) + + t.Run("on schema object with current grants", func(t *testing.T) { + opts := defaultOpts() + opts.CurrentGrants = &OwnershipCurrentGrants{ + OutboundPrivileges: Copy, + } + assertOptsValidAndSQLEquals(t, opts, `GRANT OWNERSHIP ON TABLE %s TO ROLE %s COPY CURRENT GRANTS`, tableId.FullyQualifiedName(), roleId.FullyQualifiedName()) + }) +} + func TestGrantShow(t *testing.T) { t.Run("no options", func(t *testing.T) { opts := &ShowGrantOptions{} diff --git a/pkg/sdk/grants_validations.go b/pkg/sdk/grants_validations.go index e609ad9de8..6ef10df215 100644 --- a/pkg/sdk/grants_validations.go +++ b/pkg/sdk/grants_validations.go @@ -14,6 +14,7 @@ var ( _ validatable = new(RevokePrivilegesFromDatabaseRoleOptions) _ validatable = new(grantPrivilegeToShareOptions) _ validatable = new(revokePrivilegeFromShareOptions) + _ validatable = new(GrantOwnershipOptions) _ validatable = new(ShowGrantOptions) ) @@ -267,6 +268,40 @@ func (v *OnView) validate() error { return nil } +func (opts *GrantOwnershipOptions) validate() error { + if err := opts.On.validate(); err != nil { + return err + } + if err := opts.To.validate(); err != nil { + return err + } + return nil +} + +func (v *OwnershipGrantOn) validate() error { + if !exactlyOneValueSet(v.Object, v.All, v.Future) { + return errExactlyOneOf("Object", "AllIn", "Future") + } + if valueSet(v.All) { + if err := v.All.validate(); err != nil { + return err + } + } + if valueSet(v.Future) { + if err := v.Future.validate(); err != nil { + return err + } + } + return nil +} + +func (v *OwnershipGrantTo) validate() error { + if !exactlyOneValueSet(v.DatabaseRoleName, v.AccountRoleName) { + return errExactlyOneOf("databaseRoleName", "accountRoleName") + } + return nil +} + // TODO: add validations for ShowGrantsOn, ShowGrantsTo, ShowGrantsOf and ShowGrantsIn func (opts *ShowGrantOptions) validate() error { if everyValueNil(opts.On, opts.To, opts.Of, opts.In) { diff --git a/pkg/sdk/privileges.go b/pkg/sdk/privileges.go index f5dd40a886..6249ce36f1 100644 --- a/pkg/sdk/privileges.go +++ b/pkg/sdk/privileges.go @@ -155,6 +155,8 @@ func (p SchemaPrivilege) String() string { type SchemaObjectPrivilege string const ( + SchemaObjectOwnership SchemaObjectPrivilege = "OWNERSHIP" + // -- For ALERT // OPERATE [ , ... ] SchemaObjectPrivilegeOperate SchemaObjectPrivilege = "OPERATE"