merge with main

This commit is contained in:
Will Browne
2025-03-20 13:29:46 +00:00
185 changed files with 8247 additions and 4833 deletions
+2 -3
View File
@@ -4776,10 +4776,9 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "2"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "3"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "4"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "5"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "5"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "6"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "7"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "8"]
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "7"]
],
"public/app/features/plugins/admin/components/InstallControls/index.tsx:5381": [
[0, 0, 0, "Do not re-export imported variable (\`./InstallControlsButton\`)", "0"],
+65
View File
@@ -0,0 +1,65 @@
module swagger
go 1.24.1
tool github.com/go-swagger/go-swagger/cmd/swagger
require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-openapi/analysis v0.23.0 // indirect
github.com/go-openapi/errors v0.22.0 // indirect
github.com/go-openapi/inflect v0.21.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/loads v0.22.0 // indirect
github.com/go-openapi/runtime v0.28.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/strfmt v0.23.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-openapi/validate v0.24.0 // indirect
github.com/go-swagger/go-swagger v0.30.6-0.20240310114303-db51e79a0e37 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/handlers v1.5.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/jessevdk/go-flags v1.5.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.18.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/toqueteos/webbrowser v1.2.0 // indirect
go.mongodb.org/mongo-driver v1.14.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+181
View File
@@ -0,0 +1,181 @@
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo=
github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w=
github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE=
github.com/go-openapi/inflect v0.21.0 h1:FoBjBTQEcbg2cJUWX6uwL9OyIW8eqc9k4KhN4lfbeYk=
github.com/go-openapi/inflect v0.21.0/go.mod h1:INezMuUu7SJQc2AyR3WO0DqqYUJSj8Kb4hBd7WtjlAw=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco=
github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs=
github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ=
github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc=
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c=
github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=
github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
github.com/go-swagger/go-swagger v0.30.6-0.20240310114303-db51e79a0e37 h1:KFcZmKdZmapAog2+eL1buervAYrYolBZk7fMecPPDmo=
github.com/go-swagger/go-swagger v0.30.6-0.20240310114303-db51e79a0e37/go.mod h1:i1/E+d8iPNReSE7y04FaVu5OPKB3il5cn+T1Egogg3I=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ=
github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+1
View File
@@ -317,6 +317,7 @@
/Makefile @grafana/grafana-developer-enablement-squad
/scripts/build/ @grafana/grafana-developer-enablement-squad
/scripts/list-release-artifacts.sh @grafana/grafana-developer-enablement-squad
/scripts/releasefinder.sh @baldm0mma
/.trivyignore @grafana/grafana-backend-services-squad
# OSS Plugin Partnerships backend code
+1
View File
@@ -67,6 +67,7 @@ COPY .citools/lefthook .citools/lefthook
COPY .citools/jb .citools/jb
COPY .citools/drone .citools/drone
COPY .citools/golangci-lint .citools/golangci-lint
COPY .citools/swagger ./citools/swagger
# Include vendored dependencies
COPY pkg/util/xorm pkg/util/xorm
+8 -9
View File
@@ -60,15 +60,15 @@ NGALERT_SPEC_TARGET = pkg/services/ngalert/api/tooling/api.json
$(NGALERT_SPEC_TARGET):
+$(MAKE) -C pkg/services/ngalert/api/tooling api.json
$(MERGED_SPEC_TARGET): swagger-oss-gen swagger-enterprise-gen $(NGALERT_SPEC_TARGET) $(SWAGGER) ## Merge generated and ngalert API specs
$(MERGED_SPEC_TARGET): swagger-oss-gen swagger-enterprise-gen $(NGALERT_SPEC_TARGET) ## Merge generated and ngalert API specs
# known conflicts DsPermissionType, AddApiKeyCommand, Json, Duration (identical models referenced by both specs)
$(SWAGGER) mixin -q $(SPEC_TARGET) $(ENTERPRISE_SPEC_TARGET) $(NGALERT_SPEC_TARGET) --ignore-conflicts -o $(MERGED_SPEC_TARGET)
GODEBUG=gotypesalias=0 $(GO) tool swagger mixin -q $(SPEC_TARGET) $(ENTERPRISE_SPEC_TARGET) $(NGALERT_SPEC_TARGET) --ignore-conflicts -o $(MERGED_SPEC_TARGET)
.PHONY: swagger-oss-gen
swagger-oss-gen: $(SWAGGER) ## Generate API Swagger specification
swagger-oss-gen: ## Generate API Swagger specification
@echo "re-generating swagger for OSS"
rm -f $(SPEC_TARGET)
SWAGGER_GENERATE_EXTENSION=false $(SWAGGER) generate spec -q -m -w pkg/server -o $(SPEC_TARGET) \
SWAGGER_GENERATE_EXTENSION=false GODEBUG=gotypesalias=0 $(GO) tool swagger generate spec -q -m -w pkg/server -o $(SPEC_TARGET) \
-x "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" \
-x "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" \
-x "github.com/prometheus/alertmanager" \
@@ -83,10 +83,10 @@ ifeq ("$(wildcard $(ENTERPRISE_EXT_FILE))","") ## if enterprise is not enabled
swagger-enterprise-gen:
@echo "skipping re-generating swagger for enterprise: not enabled"
else
swagger-enterprise-gen: $(SWAGGER) ## Generate API Swagger specification
swagger-enterprise-gen: ## Generate API Swagger specification
@echo "re-generating swagger for enterprise"
rm -f $(ENTERPRISE_SPEC_TARGET)
SWAGGER_GENERATE_EXTENSION=false $(SWAGGER) generate spec -q -m -w pkg/server -o $(ENTERPRISE_SPEC_TARGET) \
SWAGGER_GENERATE_EXTENSION=false GODEBUG=gotypesalias=0 $(GO) tool swagger generate spec -q -m -w pkg/server -o $(ENTERPRISE_SPEC_TARGET) \
-x "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" \
-x "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" \
-x "github.com/prometheus/alertmanager" \
@@ -99,8 +99,8 @@ endif
swagger-gen: gen-go $(MERGED_SPEC_TARGET) swagger-validate
.PHONY: swagger-validate
swagger-validate: $(MERGED_SPEC_TARGET) $(SWAGGER) ## Validate API spec
$(SWAGGER) validate --skip-warnings $(<)
swagger-validate: $(MERGED_SPEC_TARGET) # Validate API spec
GODEBUG=gotypesalias=0 $(GO) tool swagger validate --skip-warnings $(<)
.PHONY: swagger-clean
swagger-clean:
@@ -441,7 +441,6 @@ protobuf: ## Compile protobuf definitions
go install google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.4.0
buf generate pkg/plugins/backendplugin/pluginextensionv2 --template pkg/plugins/backendplugin/pluginextensionv2/buf.gen.yaml
buf generate pkg/plugins/backendplugin/secretsmanagerplugin --template pkg/plugins/backendplugin/secretsmanagerplugin/buf.gen.yaml
buf generate pkg/apis/secret/v0alpha1/decrypt --template pkg/apis/secret/v0alpha1/decrypt/buf.gen.yaml
buf generate pkg/storage/unified/resource --template pkg/storage/unified/resource/buf.gen.yaml
buf generate pkg/services/authz/proto/v1 --template pkg/services/authz/proto/v1/buf.gen.yaml
+1 -1
View File
@@ -119,7 +119,7 @@ For example:
Today it's only possible to provision data sources and dashboards, but we want to support it throughout Grafana in the future.
### 109 - Use `context.Context`
## 109 - Use `context.Context`
**State:** Completed.
@@ -50,7 +50,7 @@ This feature addresses two common challenges faced by Grafana users:
LBAC for data sources lets Grafana Teams use the same dashboard with different access control rules.
To set up LBAC for data sources for a Loki data source, refer to [Configure LBAC for Loki Data Source](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/administration/data-source-management/teamlbac/configure-teamlbac-for-loki/).
To set up LBAC for data sources for a Loki data source, refer to [Configure LBAC for Prometheus Data Source](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/administration/data-source-management/teamlbac/configure-teamlbac-for-prometheus/).
To set up LBAC for data sources for a Prometheus data source, refer to [Configure LBAC for Prometheus Data Source](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/administration/data-source-management/teamlbac/configure-teamlbac-for-prometheus/).
## Logs and Metrics Filtering with LBAC
@@ -63,6 +63,8 @@ refs:
You can use a service account to run automated workloads in Grafana, such as dashboard provisioning, configuration, or report generation. Create service accounts and tokens to authenticate applications, such as Terraform, with the Grafana API.
A service account is not the same as a Grafana [user](/docs/grafana/latest/administration/user-management/). The role of a service account is primarily to automate work using the Grafana API.
{{< admonition type="note" >}}
Service accounts replace [API keys](ref:migrate-api-keys) as the primary way to authenticate applications that interact with Grafana.
{{< /admonition >}}
@@ -41,7 +41,7 @@ refs:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/dashboards/share-dashboards-panels/shared-dashboards/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana/grafana-cloud/visualizations/dashboards/share-dashboards-panels/shared-dashboards/
destination: /docs/grafana-cloud/visualizations/dashboards/share-dashboards-panels/shared-dashboards/
configure-report:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/dashboards/create-reports/#create-a-report
@@ -55,6 +55,42 @@ Only users with the organization administrator role can add data sources.
Once you've added the Google Cloud Monitoring data source, you can [configure it](#configure-the-data-source) so that your Grafana instance's users can create queries in its [query editor](query-editor/) and apply [annotations](#annotations) when they [build dashboards](ref:build-dashboards) and use [Explore](ref:explore).
## Before you begin
Complete the following before you configure the data source.
### Configure Google authentication
Before you can request data from Google Cloud Monitoring, you must configure authentication.
All requests to Google APIs are performed on the server-side by the Grafana backend.
For authentication options and configuration details, refer to [Google authentication](google-authentication/).
When configuring Google authentication, keep in mind the following additional steps related to Google Cloud Monitoring.
#### Configure a GCP Service Account
When you [create a Google Cloud Platform (GCP) Service Account and key file](google-authentication/#create-a-gcp-service-account-and-key-file), the Service Account must have the **Monitoring Viewer** role (**Role > Select a role > Monitoring > Monitoring Viewer**):
{{< figure src="/static/img/docs/v71/cloudmonitoring_service_account_choose_role.png" max-width="600px" class="docs-image--no-shadow" caption="Choose role" >}}
#### Grant the GCE Default Service Account scope
If Grafana is running on a Google Compute Engine (GCE) virtual machine, when you [Configure a GCE Default Service Account](google-authentication/#configure-a-gce-default-service-account), you must also grant that Service Account access to the "Cloud Monitoring API" scope.
### Enable necessary Google Cloud Platform APIs
Before you can request data from Google Cloud Monitoring, you must first enable necessary APIs on the Google end.
1. Open the Monitoring and Cloud Resource Manager API pages:
- [Monitoring API](https://console.cloud.google.com/apis/library/monitoring.googleapis.com)
- [Cloud Resource Manager API](https://console.cloud.google.com/apis/library/cloudresourcemanager.googleapis.com)
1. On each page, click the `Enable` button.
{{< figure src="/static/img/docs/v71/cloudmonitoring_enable_api.png" max-width="450px" class="docs-image--no-shadow" caption="Enable GCP APIs" >}}
## Configure the data source
To configure basic settings for the data source, complete the following steps:
@@ -73,41 +109,9 @@ To configure basic settings for the data source, complete the following steps:
| **Name** | Sets the name you use to refer to the data source in panels and queries. |
| **Default** | Sets whether the data source is pre-selected for new panels. |
### Configure Google authentication
Before you can request data from Google Cloud Monitoring, you must configure authentication.
All requests to Google APIs are performed on the server-side by the Grafana backend.
For authentication options and configuration details, refer to [Google authentication](google-authentication/).
When configuring Google authentication, note these additional Google Cloud Monitoring-specific steps:
#### Configure a GCP Service Account
When you [create a Google Cloud Platform (GCP) Service Account and key file](google-authentication/#create-a-gcp-service-account-and-key-file), the Service Account must have the **Monitoring Viewer** role (**Role > Select a role > Monitoring > Monitoring Viewer**):
{{< figure src="/static/img/docs/v71/cloudmonitoring_service_account_choose_role.png" max-width="600px" class="docs-image--no-shadow" caption="Choose role" >}}
#### Grant the GCE Default Service Account scope
If Grafana is running on a Google Compute Engine (GCE) virtual machine, then when you [Configure a GCE Default Service Account](google-authentication/#configure-a-gce-default-service-account), you must also grant that Service Account access to the "Cloud Monitoring API" scope.
### Enable necessary Google Cloud Platform APIs
Before you can request data from Google Cloud Monitoring, you must first enable necessary APIs on the Google end.
1. Open the Monitoring and Cloud Resource Manager API pages:
- [Monitoring API](https://console.cloud.google.com/apis/library/monitoring.googleapis.com)
- [Cloud Resource Manager API](https://console.cloud.google.com/apis/library/cloudresourcemanager.googleapis.com)
1. On each page, click the `Enable` button.
{{< figure src="/static/img/docs/v71/cloudmonitoring_enable_api.png" max-width="450px" class="docs-image--no-shadow" caption="Enable GCP APIs" >}}
### Provision the data source
You can define and configure the data source in YAML files as part of Grafana's provisioning system.
You can define and configure the data source in YAML files as part of the Grafana provisioning system.
For more information about provisioning, and for available configuration options, refer to [Provisioning Grafana](ref:provisioning-data-sources).
#### Provisioning examples
@@ -10,12 +10,12 @@
"id": {
"type": "string",
"description": "Unique name of the plugin. If the plugin is published on grafana.com, then the plugin `id` has to follow the naming conventions.",
"pattern": "^[0-9a-z]+\\-([0-9a-z]+\\-)?(app|panel|datasource|secretsmanager)$"
"pattern": "^[0-9a-z]+\\-([0-9a-z]+\\-)?(app|panel|datasource)$"
},
"type": {
"type": "string",
"description": "Plugin type.",
"enum": ["app", "datasource", "panel", "renderer", "secretsmanager"]
"enum": ["app", "datasource", "panel", "renderer"]
},
"info": {
"type": "object",
@@ -183,11 +183,11 @@
"properties": {
"id": {
"type": "string",
"pattern": "^[0-9a-z]+\\-([0-9a-z]+\\-)?(app|panel|datasource|secretsmanager)$"
"pattern": "^[0-9a-z]+\\-([0-9a-z]+\\-)?(app|panel|datasource)$"
},
"type": {
"type": "string",
"enum": ["app", "datasource", "panel", "secretsmanager"]
"enum": ["app", "datasource", "panel"]
},
"name": {
"type": "string"
@@ -280,7 +280,7 @@
},
"type": {
"type": "string",
"enum": ["dashboard", "page", "panel", "datasource", "secretsmanager"]
"enum": ["dashboard", "page", "panel", "datasource"]
},
"name": {
"type": "string"
@@ -647,7 +647,7 @@
"id": {
"type": "string",
"description": "A unique identifier for your exposed component. This is used to reference the component in other plugins. It must be in the following format: '{PLUGIN_ID}/name-of-component/v1'.",
"pattern": "^[0-9a-z]+-([0-9a-z]+-)?(app|panel|datasource|secretsmanager)\\/[a-zA-Z0-9_-]+\\/v[0-9_.-]+$"
"pattern": "^[0-9a-z]+-([0-9a-z]+-)?(app|panel|datasource)\\/[a-zA-Z0-9_-]+\\/v[0-9_.-]+$"
},
"title": {
"type": "string",
@@ -670,7 +670,7 @@
"id": {
"type": "string",
"description": "A unique identifier for your extension point. This is used to reference the extension point in other plugins. It must be in the following format: '{PLUGIN_ID}/name-of-my-extension-point/v1'.",
"pattern": "^[0-9a-z]+-([0-9a-z]+-)?(app|panel|datasource|secretsmanager)\\/[a-zA-Z0-9_-]+\\/v[0-9_.-]+$"
"pattern": "^[0-9a-z]+-([0-9a-z]+-)?(app|panel|datasource)\\/[a-zA-Z0-9_-]+\\/v[0-9_.-]+$"
},
"title": {
"type": "string",
@@ -73,6 +73,7 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
| `newFiltersUI` | Enables new combobox style UI for the Ad hoc filters variable in scenes architecture | Yes |
| `alertingQueryAndExpressionsStepMode` | Enables step mode for alerting queries and expressions | Yes |
| `useSessionStorageForRedirection` | Use session storage for handling the redirection after login | Yes |
| `pluginsSriChecks` | Enables SRI checks for plugin assets | |
| `userStorageAPI` | Enables the user storage API | Yes |
| `azureMonitorDisableLogLimit` | Disables the log limit restriction for Azure Monitor when true. The limit is enabled by default. | |
| `preinstallAutoUpdate` | Enables automatic updates for pre-installed plugins | Yes |
@@ -147,7 +148,6 @@ Experimental features might be changed or removed without prior notice.
| `alertStateHistoryLokiOnly` | Disable Grafana alerts from emitting annotations when a remote Loki instance is available. |
| `extraThemes` | Enables extra themes |
| `lokiPredefinedOperations` | Adds predefined query operations to Loki query editor |
| `frontendSandboxMonitorOnly` | Enables monitor only in the plugin frontend sandbox (if enabled) |
| `awsDatasourcesTempCredentials` | Support temporary security credentials in AWS plugins for Grafana Cloud customers |
| `mlExpressions` | Enable support for Machine Learning in server-side expressions |
| `metricsSummary` | Enables metrics summary queries in the Tempo data source |
@@ -203,7 +203,6 @@ Experimental features might be changed or removed without prior notice.
| `homeSetupGuide` | Used in Home for users who want to return to the onboarding flow or quickly find popular config pages |
| `appSidecar` | Enable the app sidecar feature that allows rendering 2 apps at the same time |
| `rolePickerDrawer` | Enables the new role picker drawer design |
| `pluginsSriChecks` | Enables SRI checks for plugin assets |
| `unifiedStorageBigObjectsSupport` | Enables to save big objects in blob storage |
| `timeRangeProvider` | Enables time pickers sync |
| `prometheusUsesCombobox` | Use new combobox component for Prometheus query editor |
@@ -118,6 +118,18 @@ Example:
hide_version = true
```
### Enable auth for metrics
By default, metrics from Grafana itself can be accessed without authentication. This can lead to inadvertent information leakage.
To enable basic authentication for the metrics endpoint:
```toml
# If both are set, basic auth will be required for the metrics endpoints
basic_auth_username =
basic_auth_password =
```
### Enforce domain verification
If set to `true`, the Grafana server redirects requests that have a Host-header value that is mismatched to the actual domain. This might help to mitigate some DNS rebinding attacks.
+13 -12
View File
@@ -33,8 +33,8 @@ require (
github.com/beevik/etree v1.4.1 // @grafana/grafana-backend-group
github.com/benbjohnson/clock v1.3.5 // @grafana/alerting-backend
github.com/blang/semver/v4 v4.0.0 // indirect; @grafana/grafana-developer-enablement-squad
github.com/blevesearch/bleve/v2 v2.4.3 // @grafana/grafana-search-and-storage
github.com/blevesearch/bleve_index_api v1.1.12 // @grafana/grafana-search-and-storage
github.com/blevesearch/bleve/v2 v2.4.4-0.20250319135056-b82baf10b205 // @grafana/grafana-search-and-storage
github.com/blevesearch/bleve_index_api v1.2.3 // @grafana/grafana-search-and-storage
github.com/blugelabs/bluge v0.2.2 // @grafana/grafana-backend-group
github.com/blugelabs/bluge_segment_api v0.2.0 // @grafana/grafana-backend-group
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // @grafana/grafana-backend-group
@@ -124,7 +124,7 @@ require (
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c //@grafana/identity-access-team
github.com/mocktools/go-smtp-mock/v2 v2.3.1 // @grafana/grafana-backend-group
github.com/modern-go/reflect2 v1.0.2 // @grafana/alerting-backend
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // @grafana/alerting-backend
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect; @grafana/alerting-backend
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // @grafana/grafana-operator-experience-squad
github.com/olekukonko/tablewriter v0.0.5 // @grafana/grafana-backend-group
github.com/openfga/api/proto v0.0.0-20250127102726-f9709139a369 // @grafana/identity-access-team
@@ -288,21 +288,21 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.12.0 // indirect
github.com/blevesearch/geo v0.1.20 // indirect
github.com/blevesearch/go-faiss v1.0.23 // indirect
github.com/blevesearch/go-faiss v1.0.24 // indirect
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
github.com/blevesearch/gtreap v0.1.1 // indirect
github.com/blevesearch/mmap-go v1.0.4 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.2.16 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.3.5 // indirect
github.com/blevesearch/segment v0.9.1 // indirect
github.com/blevesearch/snowballstem v0.9.0 // indirect
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
github.com/blevesearch/vellum v1.0.10 // indirect
github.com/blevesearch/zapx/v11 v11.3.10 // indirect
github.com/blevesearch/zapx/v12 v12.3.10 // indirect
github.com/blevesearch/zapx/v13 v13.3.10 // indirect
github.com/blevesearch/zapx/v14 v14.3.10 // indirect
github.com/blevesearch/zapx/v15 v15.3.16 // indirect
github.com/blevesearch/zapx/v16 v16.1.8 // indirect
github.com/blevesearch/vellum v1.1.0 // indirect
github.com/blevesearch/zapx/v11 v11.4.1 // indirect
github.com/blevesearch/zapx/v12 v12.4.1 // indirect
github.com/blevesearch/zapx/v13 v13.4.1 // indirect
github.com/blevesearch/zapx/v14 v14.4.1 // indirect
github.com/blevesearch/zapx/v15 v15.4.1 // indirect
github.com/blevesearch/zapx/v16 v16.2.2-0.20250305220028-89edb0ef9aa9 // indirect
github.com/blugelabs/ice v1.0.0 // indirect
github.com/blugelabs/ice/v2 v2.0.1 // indirect
github.com/bufbuild/protocompile v0.4.0 // indirect
@@ -561,6 +561,7 @@ require (
require (
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect
github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
github.com/bluele/gcache v0.0.2 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.32.3 // indirect
github.com/open-feature/go-sdk-contrib/providers/ofrep v0.1.5 // indirect
+25 -22
View File
@@ -765,6 +765,8 @@ github.com/RoaringBitmap/roaring v0.9.1/go.mod h1:h1B7iIUOmnAeb5ytYMvnHJwxMc6LUr
github.com/RoaringBitmap/roaring v0.9.4/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA=
github.com/RoaringBitmap/roaring v1.9.3 h1:t4EbC5qQwnisr5PrP9nt0IRhRTb9gMUgQF4t4S2OByM=
github.com/RoaringBitmap/roaring v1.9.3/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg=
github.com/RoaringBitmap/roaring/v2 v2.4.5/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f h1:HR5nRmUQgXrwqZOwZ2DAc/aCi3Bu3xENpspW935vxu0=
@@ -898,14 +900,14 @@ github.com/bits-and-blooms/bitset v1.12.0 h1:U/q1fAF7xXRhFCrhROzIfffYnu+dlS38vCZ
github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/blevesearch/bleve/v2 v2.4.3 h1:XDYj+1prgX84L2Cf+V3ojrOPqXxy0qxyd2uLMmeuD+4=
github.com/blevesearch/bleve/v2 v2.4.3/go.mod h1:hEPDPrbYw3vyrm5VOa36GyS4bHWuIf4Fflp7460QQXY=
github.com/blevesearch/bleve_index_api v1.1.12 h1:P4bw9/G/5rulOF7SJ9l4FsDoo7UFJ+5kexNy1RXfegY=
github.com/blevesearch/bleve_index_api v1.1.12/go.mod h1:PbcwjIcRmjhGbkS/lJCpfgVSMROV6TRubGGAODaK1W8=
github.com/blevesearch/bleve/v2 v2.4.4-0.20250319135056-b82baf10b205 h1:u6DQJ1k4FKwRNtsrVhIRQenNdtz31way7/LgWCluFzA=
github.com/blevesearch/bleve/v2 v2.4.4-0.20250319135056-b82baf10b205/go.mod h1:nSmFOQ7M264rKoM3jf63Gl2G+ylCgZGovPgL6ZEQYzU=
github.com/blevesearch/bleve_index_api v1.2.3 h1:aJJPhTb30zc68qg2HbvCZLAF1Eh5Hhm296mDAUFaUeg=
github.com/blevesearch/bleve_index_api v1.2.3/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0=
github.com/blevesearch/geo v0.1.20 h1:paaSpu2Ewh/tn5DKn/FB5SzvH0EWupxHEIwbCk/QPqM=
github.com/blevesearch/geo v0.1.20/go.mod h1:DVG2QjwHNMFmjo+ZgzrIq2sfCh6rIHzy9d9d0B59I6w=
github.com/blevesearch/go-faiss v1.0.23 h1:Wmc5AFwDLKGl2L6mjLX1Da3vCL0EKa2uHHSorcIS1Uc=
github.com/blevesearch/go-faiss v1.0.23/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
github.com/blevesearch/go-faiss v1.0.24 h1:K79IvKjoKHdi7FdiXEsAhxpMuns0x4fM0BO93bW5jLI=
github.com/blevesearch/go-faiss v1.0.24/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y=
@@ -914,8 +916,8 @@ github.com/blevesearch/mmap-go v1.0.2/go.mod h1:ol2qBqYaOUsGdm7aRMRrYGgPvnwLe6Y+
github.com/blevesearch/mmap-go v1.0.3/go.mod h1:pYvKl/grLQrBxuaRYgoTssa4rVujYYeenDp++2E+yvs=
github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc=
github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs=
github.com/blevesearch/scorch_segment_api/v2 v2.2.16 h1:uGvKVvG7zvSxCwcm4/ehBa9cCEuZVE+/zvrSl57QUVY=
github.com/blevesearch/scorch_segment_api/v2 v2.2.16/go.mod h1:VF5oHVbIFTu+znY1v30GjSpT5+9YFs9dV2hjvuh34F0=
github.com/blevesearch/scorch_segment_api/v2 v2.3.5 h1:t6NVTdS8xp1xHMJoe5WjxdZWLKhsIIb6kB0U49rhAno=
github.com/blevesearch/scorch_segment_api/v2 v2.3.5/go.mod h1:7gG1vsL3lu2L+7RuEtZ7PJhxGYE+AV9zjxTSHuXFMdM=
github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ=
github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU=
github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw=
@@ -925,20 +927,20 @@ github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMG
github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ=
github.com/blevesearch/vellum v1.0.5/go.mod h1:atE0EH3fvk43zzS7t1YNdNC7DbmcC3uz+eMD5xZ2OyQ=
github.com/blevesearch/vellum v1.0.7/go.mod h1:doBZpmRhwTsASB4QdUZANlJvqVAUdUyX0ZK7QJCTeBE=
github.com/blevesearch/vellum v1.0.10 h1:HGPJDT2bTva12hrHepVT3rOyIKFFF4t7Gf6yMxyMIPI=
github.com/blevesearch/vellum v1.0.10/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k=
github.com/blevesearch/zapx/v11 v11.3.10 h1:hvjgj9tZ9DeIqBCxKhi70TtSZYMdcFn7gDb71Xo/fvk=
github.com/blevesearch/zapx/v11 v11.3.10/go.mod h1:0+gW+FaE48fNxoVtMY5ugtNHHof/PxCqh7CnhYdnMzQ=
github.com/blevesearch/zapx/v12 v12.3.10 h1:yHfj3vXLSYmmsBleJFROXuO08mS3L1qDCdDK81jDl8s=
github.com/blevesearch/zapx/v12 v12.3.10/go.mod h1:0yeZg6JhaGxITlsS5co73aqPtM04+ycnI6D1v0mhbCs=
github.com/blevesearch/zapx/v13 v13.3.10 h1:0KY9tuxg06rXxOZHg3DwPJBjniSlqEgVpxIqMGahDE8=
github.com/blevesearch/zapx/v13 v13.3.10/go.mod h1:w2wjSDQ/WBVeEIvP0fvMJZAzDwqwIEzVPnCPrz93yAk=
github.com/blevesearch/zapx/v14 v14.3.10 h1:SG6xlsL+W6YjhX5N3aEiL/2tcWh3DO75Bnz77pSwwKU=
github.com/blevesearch/zapx/v14 v14.3.10/go.mod h1:qqyuR0u230jN1yMmE4FIAuCxmahRQEOehF78m6oTgns=
github.com/blevesearch/zapx/v15 v15.3.16 h1:Ct3rv7FUJPfPk99TI/OofdC+Kpb4IdyfdMH48sb+FmE=
github.com/blevesearch/zapx/v15 v15.3.16/go.mod h1:Turk/TNRKj9es7ZpKK95PS7f6D44Y7fAFy8F4LXQtGg=
github.com/blevesearch/zapx/v16 v16.1.8 h1:Bxzpw6YQpFs7UjoCV1+RvDw6fmAT2GZxldwX8b3wVBM=
github.com/blevesearch/zapx/v16 v16.1.8/go.mod h1:JqQlOqlRVaYDkpLIl3JnKql8u4zKTNlVEa3nLsi0Gn8=
github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w=
github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y=
github.com/blevesearch/zapx/v11 v11.4.1 h1:qFCPlFbsEdwbbckJkysptSQOsHn4s6ZOHL5GMAIAVHA=
github.com/blevesearch/zapx/v11 v11.4.1/go.mod h1:qNOGxIqdPC1MXauJCD9HBG487PxviTUUbmChFOAosGs=
github.com/blevesearch/zapx/v12 v12.4.1 h1:K77bhypII60a4v8mwvav7r4IxWA8qxhNjgF9xGdb9eQ=
github.com/blevesearch/zapx/v12 v12.4.1/go.mod h1:QRPrlPOzAxBNMI0MkgdD+xsTqx65zbuPr3Ko4Re49II=
github.com/blevesearch/zapx/v13 v13.4.1 h1:EnkEMZFUK0lsW/jOJJF2xOcp+W8TjEsyeN5BeAZEYYE=
github.com/blevesearch/zapx/v13 v13.4.1/go.mod h1:e6duBMlCvgbH9rkzNMnUa9hRI9F7ri2BRcHfphcmGn8=
github.com/blevesearch/zapx/v14 v14.4.1 h1:G47kGCshknBZzZAtjcnIAMn3oNx8XBLxp8DMq18ogyE=
github.com/blevesearch/zapx/v14 v14.4.1/go.mod h1:O7sDxiaL2r2PnCXbhh1Bvm7b4sP+jp4unE9DDPWGoms=
github.com/blevesearch/zapx/v15 v15.4.1 h1:B5IoTMUCEzFdc9FSQbhVOxAY+BO17c05866fNruiI7g=
github.com/blevesearch/zapx/v15 v15.4.1/go.mod h1:b/MreHjYeQoLjyY2+UaM0hGZZUajEbE0xhnr1A2/Q6Y=
github.com/blevesearch/zapx/v16 v16.2.2-0.20250305220028-89edb0ef9aa9 h1:a5GLOQkJqCnpD/lTfNiEVQnJ1e2CVOR8JFO6u1D7bLE=
github.com/blevesearch/zapx/v16 v16.2.2-0.20250305220028-89edb0ef9aa9/go.mod h1:v8B9VjBtELr2qRO0f3RoRG8RAZg3Jml/5SYuBT0hvGw=
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
github.com/blugelabs/bluge v0.2.2 h1:gat8CqE6P6tOgeX30XGLOVNTC26cpM2RWVcreXWtYcM=
@@ -3429,6 +3431,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
+5
View File
@@ -12,6 +12,7 @@ use (
./.citools/golangci-lint
./.citools/jb
./.citools/lefthook
./.citools/swagger
./apps/advisor
./apps/alerting/notifications
./apps/dashboard
@@ -41,3 +42,7 @@ replace github.com/getkin/kin-openapi => github.com/getkin/kin-openapi v0.126.0
replace github.com/prometheus/alertmanager => github.com/grafana/prometheus-alertmanager v0.25.1-0.20240930132144-b5e64e81e8d3
replace github.com/crewjam/saml => github.com/grafana/saml v0.4.15-0.20240917091248-ae3bbdad8a56
// Can be removed once Bleve releases v2.5.0.
// We've pinned this for: https://github.com/blevesearch/bleve/commit/af9e3111dadfedf9d30f0448506b4a57fecc8550
replace github.com/blevesearch/bleve/v2 => github.com/blevesearch/bleve/v2 v2.4.4-0.20250319135056-b82baf10b205
+16 -7
View File
@@ -535,7 +535,6 @@ github.com/DataDog/sketches-go v1.4.6 h1:acd5fb+QdUzGrosfNLwrIhqyrbMORpvBy7mE+vH
github.com/DataDog/sketches-go v1.4.6/go.mod h1:7Y8GN8Jf66DLyDhc94zuWA3uHEt/7ttt8jHOBWWrSOg=
github.com/DmitriyVTitov/size v1.5.0 h1:/PzqxYrOyOUX1BXj6J9OuVRVGe+66VL4D9FlUaW515g=
github.com/DmitriyVTitov/size v1.5.0/go.mod h1:le6rNI4CoLQV1b9gzp1+3d7hMAD/uu2QcJ+aYbNgiU0=
github.com/FZambia/eagle v0.2.0/go.mod h1:LKMYBwGYhao5sJI0TppvQ4SvvldFj9gITxrl8NvGwG0=
github.com/GoogleCloudPlatform/cloudsql-proxy v1.36.0 h1:kAtNAWwvTt5+iew6baV0kbOrtjYTXPtWNSyOFlcxkBU=
github.com/GoogleCloudPlatform/cloudsql-proxy v1.36.0/go.mod h1:VRKXU8C7Y/aUKjRBTGfw0Ndv4YqNxlB8zAPJJDxbASE=
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0=
@@ -633,14 +632,26 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blevesearch/bleve_index_api v1.1.12/go.mod h1:PbcwjIcRmjhGbkS/lJCpfgVSMROV6TRubGGAODaK1W8=
github.com/blevesearch/bleve_index_api v1.2.1/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0=
github.com/blevesearch/go-faiss v1.0.23/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
github.com/blevesearch/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:kDy+zgJFJJoJYBvdfBSiZYBbdsUL0XcjHYWezpQBGPA=
github.com/blevesearch/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:9eJDeqxJ3E7WnLebQUlPD7ZjSce7AnDb9vjGmMCbD0A=
github.com/blevesearch/goleveldb v1.0.1 h1:iAtV2Cu5s0GD1lwUiekkFHe2gTMCCNVj2foPclDLIFI=
github.com/blevesearch/goleveldb v1.0.1/go.mod h1:WrU8ltZbIp0wAoig/MHbrPCXSOLpe79nz5lv5nqfYrQ=
github.com/blevesearch/scorch_segment_api/v2 v2.2.16/go.mod h1:VF5oHVbIFTu+znY1v30GjSpT5+9YFs9dV2hjvuh34F0=
github.com/blevesearch/scorch_segment_api/v2 v2.3.3/go.mod h1:LXidEjeenMdbcLKP/UdZi1HJOny61FbhslAh5SgN5Ik=
github.com/blevesearch/snowball v0.6.1 h1:cDYjn/NCH+wwt2UdehaLpr2e4BwLIjN4V/TdLsL+B5A=
github.com/blevesearch/snowball v0.6.1/go.mod h1:ZF0IBg5vgpeoUhnMza2v0A/z8m1cWPlwhke08LpNusg=
github.com/blevesearch/stempel v0.2.0 h1:CYzVPaScODMvgE9o+kf6D4RJ/VRomyi9uHF+PtB+Afc=
github.com/blevesearch/stempel v0.2.0/go.mod h1:wjeTHqQv+nQdbPuJ/YcvOjTInA2EIc6Ks1FoSUzSLvc=
github.com/blevesearch/vellum v1.0.10/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k=
github.com/blevesearch/zapx/v11 v11.3.10/go.mod h1:0+gW+FaE48fNxoVtMY5ugtNHHof/PxCqh7CnhYdnMzQ=
github.com/blevesearch/zapx/v12 v12.3.10/go.mod h1:0yeZg6JhaGxITlsS5co73aqPtM04+ycnI6D1v0mhbCs=
github.com/blevesearch/zapx/v13 v13.3.10/go.mod h1:w2wjSDQ/WBVeEIvP0fvMJZAzDwqwIEzVPnCPrz93yAk=
github.com/blevesearch/zapx/v14 v14.3.10/go.mod h1:qqyuR0u230jN1yMmE4FIAuCxmahRQEOehF78m6oTgns=
github.com/blevesearch/zapx/v15 v15.3.16/go.mod h1:Turk/TNRKj9es7ZpKK95PS7f6D44Y7fAFy8F4LXQtGg=
github.com/blevesearch/zapx/v16 v16.1.8/go.mod h1:JqQlOqlRVaYDkpLIl3JnKql8u4zKTNlVEa3nLsi0Gn8=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
github.com/bradleyjkemp/cupaloy/v2 v2.6.0 h1:knToPYa2xtfg42U3I6punFEjaGFKWQRXJwj0JTv4mTs=
@@ -660,8 +671,6 @@ github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N
github.com/bytedance/sonic v1.10.0-rc3 h1:uNSnscRapXTwUgTyOF0GVljYD08p9X/Lbr9MweSV3V0=
github.com/bytedance/sonic v1.10.0-rc3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/campoy/embedmd v1.0.0 h1:V4kI2qTJJLf4J29RzI/MAt2c3Bl4dQSYPuflzwFH2hY=
github.com/centrifugal/centrifuge v0.34.4/go.mod h1:VliWwN01/Iom9tLFJyk4E5FdKcn3cbJhnlt0XTnlolo=
github.com/centrifugal/protocol v0.16.0/go.mod h1:7V5vI30VcoxJe4UD87xi7bOsvI0bmEhvbQuMjrFM2L4=
github.com/checkpoint-restore/go-criu/v4 v4.1.0 h1:WW2B2uxx9KWF6bGlHqhm8Okiafwwx7Y2kcpn8lCpjgo=
github.com/checkpoint-restore/go-criu/v5 v5.0.0 h1:TW8f/UvntYoVDMN1K2HlT82qH1rb0sOjpGw3m6Ym+i4=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
@@ -745,6 +754,7 @@ github.com/couchbase/moss v0.2.0 h1:VCYrMzFwEryyhRSeI+/b3tRBSeTpi/8gn5Kf6dxqn+o=
github.com/couchbase/moss v0.2.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs=
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk=
github.com/creasty/defaults v1.8.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM=
github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo=
@@ -1052,6 +1062,7 @@ github.com/jon-whit/go-grpc-prometheus v1.4.0 h1:/wmpGDJcLXuEjXryWhVYEGt9YBRhtLw
github.com/jon-whit/go-grpc-prometheus v1.4.0/go.mod h1:iTPm+Iuhh3IIqR0iGZ91JJEg5ax6YQEe1I0f6vtBuao=
github.com/joncrlsn/dque v0.0.0-20211108142734-c2ef48c5192a h1:sfe532Ipn7GX0V6mHdynBk393rDmqgI0QmjLK7ct7TU=
github.com/joncrlsn/dque v0.0.0-20211108142734-c2ef48c5192a/go.mod h1:dNKs71rs2VJGBAmttu7fouEsRQlRjxy0p1Sx+T5wbpY=
github.com/json-iterator/go v0.0.0-20171115153421-f7279a603ede/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
github.com/jsternberg/zap-logfmt v1.2.0 h1:1v+PK4/B48cy8cfQbxL4FmmNZrjnIMr2BsnyEmXqv2o=
github.com/jsternberg/zap-logfmt v1.2.0/go.mod h1:kz+1CUmCutPWABnNkOu9hOHKdT2q3TDYCcsFy9hpqb0=
@@ -1120,7 +1131,6 @@ github.com/matryer/moq v0.3.3/go.mod h1:RJ75ZZZD71hejp39j4crZLsEDszGk6iH4v4YsWFK
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-shellwords v1.0.3 h1:K/VxK7SZ+cvuPgFSLKi5QPI9Vr/ipOf4C1gN+ntueUk=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
github.com/maypok86/otter v1.2.4/go.mod h1:mKLfoI7v1HOmQMwFgX4QkRk23mX6ge3RDvjdHOWG4R4=
github.com/mfridman/xflag v0.1.0 h1:TWZrZwG1QklFX5S4j1vxfF1sZbZeZSGofMwPMLAF29M=
github.com/mfridman/xflag v0.1.0/go.mod h1:/483ywM5ZO5SuMVjrIGquYNE5CzLrj5Ux/LxWWnjRaE=
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517 h1:zpIH83+oKzcpryru8ceC6BxnoG8TBrhgAvRg8obzup0=
@@ -1271,7 +1281,6 @@ github.com/rabbitmq/amqp091-go v1.9.0 h1:qrQtyzB4H8BQgEuJwhmVQqVHB9O4+MNDJCCAcpc
github.com/rabbitmq/amqp091-go v1.9.0/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/redis/rueidis v1.0.53/go.mod h1:by+34b0cFXndxtYmPAHpoTHO5NkosDlBvhexoTURIxM=
github.com/relvacode/iso8601 v1.4.0 h1:GsInVSEJfkYuirYFxa80nMLbH2aydgZpIf52gYZXUJs=
github.com/relvacode/iso8601 v1.4.0/go.mod h1:FlNp+jz+TXpyRqgmM7tnzHHzBnz776kmAH2h3sZCn0I=
github.com/richardartoul/molecule v1.0.0 h1:+LFA9cT7fn8KF39zy4dhOnwcOwRoqKiBkPqKqya+8+U=
@@ -1293,10 +1302,8 @@ github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtm
github.com/schollz/progressbar/v3 v3.14.6 h1:GyjwcWBAf+GFDMLziwerKvpuS7ZF+mNTAXIB2aspiZs=
github.com/schollz/progressbar/v3 v3.14.6/go.mod h1:Nrzpuw3Nl0srLY0VlTvC4V6RL50pcEymjy6qyJAaLa0=
github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo=
github.com/segmentio/encoding v0.4.1/go.mod h1:/d03Cd8PoaDeceuhUUUQWjU0KhWjrmYrWPgtJHYZSnI=
github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM=
github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY=
github.com/shadowspore/fossil-delta v0.0.0-20241213113458-1d797d70cbe3/go.mod h1:aJIMhRsunltJR926EB2MUg8qHemFQDreSB33pyto2Ps=
github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU=
github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
@@ -1311,6 +1318,7 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5I
github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc=
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 h1:lIOOHPEbXzO3vnmx2gok1Tfs31Q8GQqKLc8vVqyQq/I=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
@@ -1409,6 +1417,7 @@ github.com/zenazn/goji v1.0.1/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxt
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b h1:7gd+rd8P3bqcn/96gOZa3F5dpJr/vEiDQYlNb/y2uNs=
go.einride.tech/aip v0.68.0 h1:4seM66oLzTpz50u4K1zlJyOXQ3tCzcJN7I22tKkjipw=
go.einride.tech/aip v0.68.0/go.mod h1:7y9FF8VtPWqpxuAxl0KQWqaULxW4zFIesD6zF5RIHHg=
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489 h1:1JFLBqwIgdyHN1ZtgjTBwO+blA6gVOmZurpiMEsETKo=
go.etcd.io/etcd/api/v3 v3.5.12/go.mod h1:Ot+o0SWSyT6uHhA56al1oCED0JImsRiU9Dc26+C2a+4=
go.etcd.io/etcd/client/pkg/v3 v3.5.12/go.mod h1:seTzl2d9APP8R5Y2hFL3NVlD6qC/dOT+3kvrqPyTas4=
+2 -2
View File
@@ -277,8 +277,8 @@
"@grafana/prometheus": "workspace:*",
"@grafana/runtime": "workspace:*",
"@grafana/saga-icons": "workspace:*",
"@grafana/scenes": "6.3.1",
"@grafana/scenes-react": "6.3.1",
"@grafana/scenes": "6.5.2",
"@grafana/scenes-react": "6.5.2",
"@grafana/schema": "workspace:*",
"@grafana/sql": "workspace:*",
"@grafana/ui": "workspace:*",
@@ -215,7 +215,6 @@ export interface GrafanaConfig {
unifiedAlerting: UnifiedAlertingConfig;
angularSupportEnabled: boolean;
feedbackLinksEnabled: boolean;
secretsManagerPluginEnabled: boolean;
supportBundlesEnabled: boolean;
secureSocksDSProxyEnabled: boolean;
googleAnalyticsId: string | undefined;
@@ -826,6 +826,7 @@ export interface FeatureToggles {
managedDualWriter?: boolean;
/**
* Enables SRI checks for plugin assets
* @default false
*/
pluginsSriChecks?: boolean;
/**
@@ -17,7 +17,6 @@ export enum PluginType {
datasource = 'datasource',
app = 'app',
renderer = 'renderer',
secretsmanager = 'secretsmanager',
}
/** Describes status of {@link https://grafana.com/docs/grafana/latest/plugins/plugin-signatures/ | plugin signature} */
@@ -185,6 +185,40 @@ describe('Format value with value mappings', () => {
});
});
describe('Range mapping with null From or null To', () => {
expect(
getValueMappingResult(
[
{
type: MappingType.RangeToText,
options: {
from: 0,
to: null,
result: { text: 'pos' },
},
},
],
100
)
).toEqual({ text: 'pos' });
expect(
getValueMappingResult(
[
{
type: MappingType.RangeToText,
options: {
from: null,
to: 0,
result: { text: 'neg' },
},
},
],
-100
)
).toEqual({ text: 'neg' });
});
describe('Format value with regex mappings', () => {
it('should return correct regular expression result', () => {
const value = 'www.foo.com';
@@ -35,13 +35,17 @@ export function getValueMappingResult(valueMappings: ValueMapping[], value: any)
continue;
}
const isNumFrom = !isNaN(vm.options.from!);
if (isNumFrom && valueAsNumber < vm.options.from!) {
const from = vm.options.from ?? -Infinity;
const isNumFrom = !isNaN(from);
if (isNumFrom && valueAsNumber < from) {
continue;
}
const isNumTo = !isNaN(vm.options.to!);
if (isNumTo && valueAsNumber > vm.options.to!) {
const to = vm.options.to ?? Infinity;
const isNumTo = !isNaN(to);
if (isNumTo && valueAsNumber > to) {
continue;
}
-1
View File
@@ -120,7 +120,6 @@ export class GrafanaBootConfig implements GrafanaConfig {
rendererDefaultImageWidth = 1000;
rendererDefaultImageHeight = 500;
rendererDefaultImageScale = 1;
secretsManagerPluginEnabled = false;
supportBundlesEnabled = false;
http2Enabled = false;
dateFormats?: SystemDateFormatSettings;
@@ -126,5 +126,7 @@ const collapsableSectionStyles = (theme: GrafanaTheme2) => ({
}),
label: css({
display: 'flex',
fontWeight: theme.typography.fontWeightMedium,
color: theme.colors.text.maxContrast,
}),
});
-49
View File
@@ -6,7 +6,6 @@ import (
"github.com/grafana/grafana/pkg/api/response"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
skv "github.com/grafana/grafana/pkg/services/secrets/kvstore"
)
func (hs *HTTPServer) AdminRotateDataEncryptionKeys(c *contextmodel.ReqContext) response.Response {
@@ -50,51 +49,3 @@ func (hs *HTTPServer) AdminRollbackSecrets(c *contextmodel.ReqContext) response.
return response.Respond(http.StatusOK, "Secrets rolled back successfully")
}
// To migrate to the plugin, it must be installed and configured
// so as not to lose access to migrated secrets
func (hs *HTTPServer) AdminMigrateSecretsToPlugin(c *contextmodel.ReqContext) response.Response {
if skv.EvaluateRemoteSecretsPlugin(c.Req.Context(), hs.secretsPluginManager, hs.Cfg) != nil {
hs.log.Warn("Received secrets plugin migration request while plugin is not available")
return response.Respond(http.StatusBadRequest, "Secrets plugin is not available")
}
err := hs.secretsPluginMigrator.TriggerPluginMigration(c.Req.Context(), true)
if err != nil {
hs.log.Error("Failed to trigger secret migration to plugin", "error", err.Error())
return response.Respond(http.StatusInternalServerError, "Secret migration to plugin failed")
}
return response.Respond(http.StatusOK, "Secret migration to plugin triggered successfully")
}
// To migrate from the plugin, it must be installed only
// as it is possible the user disabled it and then wants to migrate
func (hs *HTTPServer) AdminMigrateSecretsFromPlugin(c *contextmodel.ReqContext) response.Response {
if hs.secretsPluginManager.SecretsManager(c.Req.Context()) == nil {
hs.log.Warn("Received secrets plugin migration request while plugin is not installed")
return response.Respond(http.StatusBadRequest, "Secrets plugin is not installed")
}
err := hs.secretsPluginMigrator.TriggerPluginMigration(c.Req.Context(), false)
if err != nil {
hs.log.Error("Failed to trigger secret migration from plugin", "error", err.Error())
return response.Respond(http.StatusInternalServerError, "Secret migration from plugin failed")
}
return response.Respond(http.StatusOK, "Secret migration from plugin triggered successfully")
}
func (hs *HTTPServer) AdminDeleteAllSecretsManagerPluginSecrets(c *contextmodel.ReqContext) response.Response {
if hs.secretsPluginManager.SecretsManager(c.Req.Context()) == nil {
hs.log.Warn("Received secrets plugin deletion request while plugin is not installed")
return response.Respond(http.StatusBadRequest, "Secrets plugin is not installed")
}
items, err := hs.secretsStore.GetAll(c.Req.Context())
if err != nil {
return response.Respond(http.StatusInternalServerError, "an error occurred while retrieving secrets")
}
for _, item := range items {
err := hs.secretsStore.Del(c.Req.Context(), *item.OrgId, *item.Namespace, *item.Type)
if err != nil {
return response.Respond(http.StatusInternalServerError, fmt.Sprintf("error deleting key with org=%v namespace=%v type=%v. error=%v", *item.OrgId, *item.Namespace, *item.Type, err.Error()))
}
}
return response.Respond(http.StatusOK, fmt.Sprintf("All %d Secrets Manager plugin secrets deleted", len(items)))
}
-3
View File
@@ -573,9 +573,6 @@ func (hs *HTTPServer) registerRoutes() {
adminRoute.Post("/encryption/reencrypt-data-keys", reqGrafanaAdmin, routing.Wrap(hs.AdminReEncryptEncryptionKeys))
adminRoute.Post("/encryption/reencrypt-secrets", reqGrafanaAdmin, routing.Wrap(hs.AdminReEncryptSecrets))
adminRoute.Post("/encryption/rollback-secrets", reqGrafanaAdmin, routing.Wrap(hs.AdminRollbackSecrets))
adminRoute.Post("/encryption/migrate-secrets/to-plugin", reqGrafanaAdmin, routing.Wrap(hs.AdminMigrateSecretsToPlugin))
adminRoute.Post("/encryption/migrate-secrets/from-plugin", reqGrafanaAdmin, routing.Wrap(hs.AdminMigrateSecretsFromPlugin))
adminRoute.Post("/encryption/delete-secretsmanagerplugin-secrets", reqGrafanaAdmin, routing.Wrap(hs.AdminDeleteAllSecretsManagerPluginSecrets))
adminRoute.Post("/provisioning/dashboards/reload", authorize(ac.EvalPermission(ActionProvisioningReload, ScopeProvisionersDashboards)), routing.Wrap(hs.AdminProvisioningReloadDashboards))
adminRoute.Post("/provisioning/plugins/reload", authorize(ac.EvalPermission(ActionProvisioningReload, ScopeProvisionersPlugins)), routing.Wrap(hs.AdminProvisioningReloadPlugins))
-18
View File
@@ -26,7 +26,6 @@ import (
)
var datasourcesLogger = log.New("datasources")
var secretsPluginError datasources.ErrDatasourceSecretsPluginUserFriendly
// swagger:route GET /datasources datasources getDataSources
//
@@ -178,9 +177,6 @@ func (hs *HTTPServer) DeleteDataSourceById(c *contextmodel.ReqContext) response.
err = hs.DataSourcesService.DeleteDataSource(c.Req.Context(), cmd)
if err != nil {
if errors.As(err, &secretsPluginError) {
return response.Error(http.StatusInternalServerError, "Failed to delete datasource: "+err.Error(), err)
}
return response.Error(http.StatusInternalServerError, "Failed to delete datasource", err)
}
@@ -257,9 +253,6 @@ func (hs *HTTPServer) DeleteDataSourceByUID(c *contextmodel.ReqContext) response
err = hs.DataSourcesService.DeleteDataSource(c.Req.Context(), cmd)
if err != nil {
if errors.As(err, &secretsPluginError) {
return response.Error(http.StatusInternalServerError, "Failed to delete datasource: "+err.Error(), err)
}
return response.Error(http.StatusInternalServerError, "Failed to delete datasource", err)
}
@@ -307,9 +300,6 @@ func (hs *HTTPServer) DeleteDataSourceByName(c *contextmodel.ReqContext) respons
cmd := &datasources.DeleteDataSourceCommand{Name: name, OrgID: c.SignedInUser.GetOrgID()}
err = hs.DataSourcesService.DeleteDataSource(c.Req.Context(), cmd)
if err != nil {
if errors.As(err, &secretsPluginError) {
return response.Error(http.StatusInternalServerError, "Failed to delete datasource: "+err.Error(), err)
}
return response.Error(http.StatusInternalServerError, "Failed to delete datasource", err)
}
@@ -405,10 +395,6 @@ func (hs *HTTPServer) AddDataSource(c *contextmodel.ReqContext) response.Respons
return response.Error(http.StatusConflict, err.Error(), err)
}
if errors.As(err, &secretsPluginError) {
return response.Error(http.StatusInternalServerError, "Failed to add datasource: "+err.Error(), err)
}
return response.ErrOrFallback(http.StatusInternalServerError, "Failed to add datasource", err)
}
@@ -532,10 +518,6 @@ func (hs *HTTPServer) updateDataSourceByID(c *contextmodel.ReqContext, ds *datas
return response.Error(http.StatusConflict, "Datasource has already been updated by someone else. Please reload and try again", err)
}
if errors.As(err, &secretsPluginError) {
return response.Error(http.StatusInternalServerError, "Failed to update datasource: "+err.Error(), err)
}
return response.ErrOrFallback(http.StatusInternalServerError, "Failed to update datasource", err)
}
-5
View File
@@ -29,11 +29,6 @@ func (hs *HTTPServer) handleQueryMetricsError(err error) *response.NormalRespons
return response.Error(http.StatusNotFound, "Data source not found", err)
}
var secretsPlugin datasources.ErrDatasourceSecretsPluginUserFriendly
if errors.As(err, &secretsPlugin) {
return response.Error(http.StatusInternalServerError, fmt.Sprint("Secrets Plugin error: ", err.Error()), err)
}
return response.ErrOrFallback(http.StatusInternalServerError, "Query data error", err)
}
-65
View File
@@ -1,7 +1,6 @@
package api
import (
"bytes"
"context"
"encoding/json"
"errors"
@@ -25,7 +24,6 @@ import (
fakeDatasources "github.com/grafana/grafana/pkg/services/datasources/fakes"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginconfig"
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings"
pluginSettings "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings/service"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
"github.com/grafana/grafana/pkg/services/query"
@@ -40,11 +38,6 @@ type fakeDataSourceRequestValidator struct {
err error
}
type secretsErrorResponseBody struct {
Error string `json:"error"`
Message string `json:"message"`
}
func (rv *fakeDataSourceRequestValidator) Validate(ds *datasources.DataSource, req *http.Request) error {
return rv.err
}
@@ -93,64 +86,6 @@ func TestAPIEndpoint_Metrics_QueryMetricsV2(t *testing.T) {
})
}
func TestAPIEndpoint_Metrics_PluginDecryptionFailure(t *testing.T) {
cfg := setting.NewCfg()
ds := &fakeDatasources.FakeDataSourceService{SimulatePluginFailure: true}
db := &dbtest.FakeDB{ExpectedError: pluginsettings.ErrPluginSettingNotFound}
pcp := plugincontext.ProvideService(cfg, localcache.ProvideService(),
&pluginstore.FakePluginStore{
PluginList: []pluginstore.Plugin{
{
JSONData: plugins.JSONData{
ID: "grafana",
},
},
},
},
&fakeDatasources.FakeCacheService{},
ds, pluginSettings.ProvideService(db, secretstest.NewFakeSecretsService()), pluginconfig.NewFakePluginRequestConfigProvider(),
)
qds := query.ProvideService(
cfg,
nil,
nil,
&fakeDataSourceRequestValidator{},
&fakePluginClient{
QueryDataHandlerFunc: func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
resp := backend.Responses{
"A": backend.DataResponse{
Error: errors.New("query failed"),
},
}
return &backend.QueryDataResponse{Responses: resp}, nil
},
},
pcp,
)
httpServer := SetupAPITestServer(t, func(hs *HTTPServer) {
hs.queryDataService = qds
hs.QuotaService = quotatest.New(false, nil)
hs.pluginContextProvider = pcp
})
t.Run("Status code is 500 and a secrets plugin error is returned if there is a problem getting secrets from the remote plugin", func(t *testing.T) {
req := httpServer.NewPostRequest("/api/ds/query", strings.NewReader(reqValid))
webtest.RequestWithSignedInUser(req, &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {datasources.ActionQuery: []string{datasources.ScopeAll}}}})
resp, err := httpServer.SendJSON(req)
require.NoError(t, err)
require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
buf := new(bytes.Buffer)
_, err = buf.ReadFrom(resp.Body)
require.NoError(t, err)
require.NoError(t, resp.Body.Close())
var resObj secretsErrorResponseBody
err = json.Unmarshal(buf.Bytes(), &resObj)
require.NoError(t, err)
require.Equal(t, "", resObj.Error)
require.Contains(t, resObj.Message, "Secrets Plugin error:")
})
}
var reqValid = `{
"from": "",
"to": "",
+23 -24
View File
@@ -220,30 +220,29 @@ type FrontendSettingsDTO struct {
LicenseInfo FrontendSettingsLicenseInfoDTO `json:"licenseInfo"`
FeatureToggles map[string]bool `json:"featureToggles"`
AnonymousEnabled bool `json:"anonymousEnabled"`
AnonymousDeviceLimit int64 `json:"anonymousDeviceLimit"`
RendererAvailable bool `json:"rendererAvailable"`
RendererVersion string `json:"rendererVersion"`
RendererDefaultImageWidth int `json:"rendererDefaultImageWidth"`
RendererDefaultImageHeight int `json:"rendererDefaultImageHeight"`
RendererDefaultImageScale float64 `json:"rendererDefaultImageScale"`
SecretsManagerPluginEnabled bool `json:"secretsManagerPluginEnabled"`
Http2Enabled bool `json:"http2Enabled"`
GrafanaJavascriptAgent setting.GrafanaJavascriptAgent `json:"grafanaJavascriptAgent"`
PluginCatalogURL string `json:"pluginCatalogURL"`
PluginAdminEnabled bool `json:"pluginAdminEnabled"`
PluginAdminExternalManageEnabled bool `json:"pluginAdminExternalManageEnabled"`
PluginCatalogHiddenPlugins []string `json:"pluginCatalogHiddenPlugins"`
PluginCatalogManagedPlugins []string `json:"pluginCatalogManagedPlugins"`
PluginCatalogPreinstalledPlugins []setting.InstallPlugin `json:"pluginCatalogPreinstalledPlugins"`
ExpressionsEnabled bool `json:"expressionsEnabled"`
AwsAllowedAuthProviders []string `json:"awsAllowedAuthProviders"`
AwsAssumeRoleEnabled bool `json:"awsAssumeRoleEnabled"`
SupportBundlesEnabled bool `json:"supportBundlesEnabled"`
SnapshotEnabled bool `json:"snapshotEnabled"`
SecureSocksDSProxyEnabled bool `json:"secureSocksDSProxyEnabled"`
ReportingStaticContext map[string]string `json:"reportingStaticContext"`
FeatureToggles map[string]bool `json:"featureToggles"`
AnonymousEnabled bool `json:"anonymousEnabled"`
AnonymousDeviceLimit int64 `json:"anonymousDeviceLimit"`
RendererAvailable bool `json:"rendererAvailable"`
RendererVersion string `json:"rendererVersion"`
RendererDefaultImageWidth int `json:"rendererDefaultImageWidth"`
RendererDefaultImageHeight int `json:"rendererDefaultImageHeight"`
RendererDefaultImageScale float64 `json:"rendererDefaultImageScale"`
Http2Enabled bool `json:"http2Enabled"`
GrafanaJavascriptAgent setting.GrafanaJavascriptAgent `json:"grafanaJavascriptAgent"`
PluginCatalogURL string `json:"pluginCatalogURL"`
PluginAdminEnabled bool `json:"pluginAdminEnabled"`
PluginAdminExternalManageEnabled bool `json:"pluginAdminExternalManageEnabled"`
PluginCatalogHiddenPlugins []string `json:"pluginCatalogHiddenPlugins"`
PluginCatalogManagedPlugins []string `json:"pluginCatalogManagedPlugins"`
PluginCatalogPreinstalledPlugins []setting.InstallPlugin `json:"pluginCatalogPreinstalledPlugins"`
ExpressionsEnabled bool `json:"expressionsEnabled"`
AwsAllowedAuthProviders []string `json:"awsAllowedAuthProviders"`
AwsAssumeRoleEnabled bool `json:"awsAssumeRoleEnabled"`
SupportBundlesEnabled bool `json:"supportBundlesEnabled"`
SnapshotEnabled bool `json:"snapshotEnabled"`
SecureSocksDSProxyEnabled bool `json:"secureSocksDSProxyEnabled"`
ReportingStaticContext map[string]string `json:"reportingStaticContext"`
PluginDependencies map[string][]plugins.Dependency `json:"pluginDependants"`
Azure FrontendSettingsAzureDTO `json:"azure"`
-3
View File
@@ -23,7 +23,6 @@ import (
"github.com/grafana/grafana/pkg/services/licensing"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
"github.com/grafana/grafana/pkg/services/secrets/kvstore"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tsdb/grafanads"
"github.com/grafana/grafana/pkg/util"
@@ -174,7 +173,6 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro
}
hasAccess := accesscontrol.HasAccess(hs.AccessControl, c)
secretsManagerPluginEnabled := kvstore.EvaluateRemoteSecretsPlugin(c.Req.Context(), hs.secretsPluginManager, hs.Cfg) == nil
trustedTypesDefaultPolicyEnabled := (hs.Cfg.CSPEnabled && strings.Contains(hs.Cfg.CSPTemplate, "require-trusted-types-for")) || (hs.Cfg.CSPReportOnlyEnabled && strings.Contains(hs.Cfg.CSPReportOnlyTemplate, "require-trusted-types-for"))
isCloudMigrationTarget := hs.Features.IsEnabled(c.Req.Context(), featuremgmt.FlagOnPremToCloudMigrations) && hs.Cfg.CloudMigration.IsTarget
featureToggles := hs.Features.GetEnabled(c.Req.Context())
@@ -280,7 +278,6 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro
RendererDefaultImageWidth: hs.Cfg.RendererDefaultImageWidth,
RendererDefaultImageHeight: hs.Cfg.RendererDefaultImageHeight,
RendererDefaultImageScale: hs.Cfg.RendererDefaultImageScale,
SecretsManagerPluginEnabled: secretsManagerPluginEnabled,
Http2Enabled: hs.Cfg.Protocol == setting.HTTP2Scheme,
GrafanaJavascriptAgent: hs.Cfg.GrafanaJavascriptAgent,
PluginCatalogURL: hs.Cfg.PluginCatalogURL,
+4 -6
View File
@@ -169,10 +169,9 @@ type HTTPServer struct {
Listener net.Listener
EncryptionService encryption.Internal
SecretsService secrets.Service
secretsPluginManager plugins.SecretsPluginManager
secretsStore secretsKV.SecretsKVStore
secretsMigrator secrets.Migrator
secretsPluginMigrator spm.SecretMigrationProvider
secretMigrationProvider spm.SecretMigrationProvider
DataSourcesService datasources.DataSourceService
cleanUpService *cleanup.CleanUpService
tracer tracing.Tracer
@@ -264,8 +263,8 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
dashboardPermissionsService accesscontrol.DashboardPermissionsService, dashboardVersionService dashver.Service,
starService star.Service, csrfService csrf.Service, managedPlugins managedplugins.Manager,
playlistService playlist.Service, apiKeyService apikey.Service, kvStore kvstore.KVStore,
secretsMigrator secrets.Migrator, secretsPluginManager plugins.SecretsPluginManager, secretsService secrets.Service,
secretsPluginMigrator spm.SecretMigrationProvider, secretsStore secretsKV.SecretsKVStore,
secretsMigrator secrets.Migrator, secretsService secrets.Service,
secretMigrationProvider spm.SecretMigrationProvider, secretsStore secretsKV.SecretsKVStore,
publicDashboardsApi *publicdashboardsApi.Api, userService user.Service, tempUserService tempUser.Service,
loginAttemptService loginAttempt.Service, orgService org.Service, orgDeletionService org.DeletionService, teamService team.Service,
accesscontrolService accesscontrol.Service, navTreeService navtree.Service,
@@ -329,9 +328,8 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
SocialService: socialService,
EncryptionService: encryptionService,
SecretsService: secretsService,
secretsPluginManager: secretsPluginManager,
secretsMigrator: secretsMigrator,
secretsPluginMigrator: secretsPluginMigrator,
secretMigrationProvider: secretMigrationProvider,
secretsStore: secretsStore,
DataSourcesService: dataSourcesService,
searchUsersService: searchUsersService,
+14 -31
View File
@@ -12,7 +12,6 @@ import (
"github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
"github.com/grafana/grafana/pkg/plugins/backendplugin/secretsmanagerplugin"
"github.com/grafana/grafana/pkg/plugins/log"
)
@@ -31,14 +30,13 @@ var handshake = goplugin.HandshakeConfig{
// pluginSet is list of plugins supported on v2.
var pluginSet = map[int]goplugin.PluginSet{
grpcplugin.ProtocolVersion: {
"diagnostics": &grpcplugin.DiagnosticsGRPCPlugin{},
"resource": &grpcplugin.ResourceGRPCPlugin{},
"data": &grpcplugin.DataGRPCPlugin{},
"stream": &grpcplugin.StreamGRPCPlugin{},
"admission": &grpcplugin.AdmissionGRPCPlugin{},
"conversion": &grpcplugin.ConversionGRPCPlugin{},
"renderer": &pluginextensionv2.RendererGRPCPlugin{},
"secretsmanager": &secretsmanagerplugin.SecretsManagerGRPCPlugin{},
"diagnostics": &grpcplugin.DiagnosticsGRPCPlugin{},
"resource": &grpcplugin.ResourceGRPCPlugin{},
"data": &grpcplugin.DataGRPCPlugin{},
"stream": &grpcplugin.StreamGRPCPlugin{},
"admission": &grpcplugin.AdmissionGRPCPlugin{},
"conversion": &grpcplugin.ConversionGRPCPlugin{},
"renderer": &pluginextensionv2.RendererGRPCPlugin{},
},
}
@@ -84,19 +82,15 @@ func newClientConfig(executablePath string, args []string, env []string, skipHos
// StartRendererFunc callback function called when a renderer plugin is started.
type StartRendererFunc func(pluginID string, renderer pluginextensionv2.RendererPlugin, logger log.Logger) error
// StartSecretsManagerFunc callback function called when a secrets manager plugin is started.
type StartSecretsManagerFunc func(pluginID string, secretsmanager secretsmanagerplugin.SecretsManagerPlugin, logger log.Logger) error
// PluginDescriptor is a descriptor used for registering backend plugins.
type PluginDescriptor struct {
pluginID string
executablePath string
executableArgs []string
skipHostEnvVars bool
managed bool
versionedPlugins map[int]goplugin.PluginSet
startRendererFn StartRendererFunc
startSecretsManagerFn StartSecretsManagerFunc
pluginID string
executablePath string
executableArgs []string
skipHostEnvVars bool
managed bool
versionedPlugins map[int]goplugin.PluginSet
startRendererFn StartRendererFunc
}
// NewBackendPlugin creates a new backend plugin factory used for registering a backend plugin.
@@ -131,14 +125,3 @@ func NewRendererPlugin(pluginID, executablePath string, startFn StartRendererFun
startRendererFn: startFn,
})
}
// NewSecretsManagerPlugin creates a new secrets manager plugin factory used for registering a backend secrets manager plugin.
func NewSecretsManagerPlugin(pluginID, executablePath string, startFn StartSecretsManagerFunc) backendplugin.PluginFactoryFunc {
return newPlugin(PluginDescriptor{
pluginID: pluginID,
executablePath: executablePath,
managed: false,
versionedPlugins: pluginSet,
startSecretsManagerFn: startFn,
})
}
@@ -16,7 +16,6 @@ import (
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
"github.com/grafana/grafana/pkg/plugins/backendplugin/secretsmanagerplugin"
"github.com/grafana/grafana/pkg/plugins/log"
)
@@ -32,7 +31,6 @@ type ClientV2 struct {
grpcplugin.AdmissionClient
grpcplugin.ConversionClient
pluginextensionv2.RendererPlugin
secretsmanagerplugin.SecretsManagerPlugin
}
func newClientV2(descriptor PluginDescriptor, logger log.Logger, rpcClient plugin.ClientProtocol) (*ClientV2, error) {
@@ -71,11 +69,6 @@ func newClientV2(descriptor PluginDescriptor, logger log.Logger, rpcClient plugi
return nil, err
}
rawSecretsManager, err := rpcClient.Dispense("secretsmanager")
if err != nil {
return nil, err
}
c := &ClientV2{}
if rawDiagnostics != nil {
if diagnosticsClient, ok := rawDiagnostics.(grpcplugin.DiagnosticsClient); ok {
@@ -119,24 +112,12 @@ func newClientV2(descriptor PluginDescriptor, logger log.Logger, rpcClient plugi
}
}
if rawSecretsManager != nil {
if secretsManagerPlugin, ok := rawSecretsManager.(secretsmanagerplugin.SecretsManagerPlugin); ok {
c.SecretsManagerPlugin = secretsManagerPlugin
}
}
if descriptor.startRendererFn != nil {
if err := descriptor.startRendererFn(descriptor.pluginID, c.RendererPlugin, logger); err != nil {
return nil, err
}
}
if descriptor.startSecretsManagerFn != nil {
if err := descriptor.startSecretsManagerFn(descriptor.pluginID, c.SecretsManagerPlugin, logger); err != nil {
return nil, err
}
}
return c, nil
}
+2 -15
View File
@@ -8,7 +8,6 @@ import (
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
"github.com/grafana/grafana/pkg/plugins/backendplugin/grpcplugin"
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
"github.com/grafana/grafana/pkg/plugins/backendplugin/secretsmanagerplugin"
"github.com/grafana/grafana/pkg/plugins/log"
)
@@ -21,7 +20,7 @@ type Service struct {
func New(providers ...PluginBackendProvider) *Service {
if len(providers) == 0 {
return New(SecretsManagerProvider, DefaultProvider)
return New(DefaultProvider)
}
return &Service{
providerChain: providers,
@@ -29,7 +28,7 @@ func New(providers ...PluginBackendProvider) *Service {
}
func ProvideService(coreRegistry *coreplugin.Registry) *Service {
return New(coreRegistry.BackendFactoryProvider(), SecretsManagerProvider, DefaultProvider)
return New(coreRegistry.BackendFactoryProvider(), DefaultProvider)
}
func (s *Service) BackendFactory(ctx context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc {
@@ -53,18 +52,6 @@ var RendererProvider PluginBackendProvider = func(_ context.Context, p *plugins.
)
}
var SecretsManagerProvider PluginBackendProvider = func(_ context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc {
if !p.IsSecretsManager() {
return nil
}
return grpcplugin.NewSecretsManagerPlugin(p.ID, p.ExecutablePath(),
func(pluginID string, secretsmanager secretsmanagerplugin.SecretsManagerPlugin, logger log.Logger) error {
p.SecretsManager = secretsmanager
return nil
},
)
}
var DefaultProvider = PluginBackendProvider(func(_ context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc {
return grpcplugin.NewBackendPlugin(p.ID, p.ExecutablePath(), p.SkipHostEnvVars)
})
@@ -1,9 +0,0 @@
version: v1
plugins:
- plugin: go
out: pkg/plugins/backendplugin/secretsmanagerplugin
opt: paths=source_relative
- plugin: go-grpc
out: pkg/plugins/backendplugin/secretsmanagerplugin
opt:
- paths=source_relative
@@ -1,7 +0,0 @@
version: v1
breaking:
use:
- FILE
lint:
use:
- DEFAULT
@@ -1,921 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.5
// protoc (unknown)
// source: secretsmanager.proto
package secretsmanagerplugin
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Key struct {
state protoimpl.MessageState `protogen:"open.v1"`
OrgId int64 `protobuf:"varint,1,opt,name=orgId,proto3" json:"orgId,omitempty"`
Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"`
Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Key) Reset() {
*x = Key{}
mi := &file_secretsmanager_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Key) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Key) ProtoMessage() {}
func (x *Key) ProtoReflect() protoreflect.Message {
mi := &file_secretsmanager_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Key.ProtoReflect.Descriptor instead.
func (*Key) Descriptor() ([]byte, []int) {
return file_secretsmanager_proto_rawDescGZIP(), []int{0}
}
func (x *Key) GetOrgId() int64 {
if x != nil {
return x.OrgId
}
return 0
}
func (x *Key) GetNamespace() string {
if x != nil {
return x.Namespace
}
return ""
}
func (x *Key) GetType() string {
if x != nil {
return x.Type
}
return ""
}
type Item struct {
state protoimpl.MessageState `protogen:"open.v1"`
Key *Key `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Item) Reset() {
*x = Item{}
mi := &file_secretsmanager_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Item) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Item) ProtoMessage() {}
func (x *Item) ProtoReflect() protoreflect.Message {
mi := &file_secretsmanager_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Item.ProtoReflect.Descriptor instead.
func (*Item) Descriptor() ([]byte, []int) {
return file_secretsmanager_proto_rawDescGZIP(), []int{1}
}
func (x *Item) GetKey() *Key {
if x != nil {
return x.Key
}
return nil
}
func (x *Item) GetValue() string {
if x != nil {
return x.Value
}
return ""
}
type GetSecretRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
KeyDescriptor *Key `protobuf:"bytes,1,opt,name=keyDescriptor,proto3" json:"keyDescriptor,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetSecretRequest) Reset() {
*x = GetSecretRequest{}
mi := &file_secretsmanager_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetSecretRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetSecretRequest) ProtoMessage() {}
func (x *GetSecretRequest) ProtoReflect() protoreflect.Message {
mi := &file_secretsmanager_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetSecretRequest.ProtoReflect.Descriptor instead.
func (*GetSecretRequest) Descriptor() ([]byte, []int) {
return file_secretsmanager_proto_rawDescGZIP(), []int{2}
}
func (x *GetSecretRequest) GetKeyDescriptor() *Key {
if x != nil {
return x.KeyDescriptor
}
return nil
}
type GetSecretResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserFriendlyError string `protobuf:"bytes,1,opt,name=userFriendlyError,proto3" json:"userFriendlyError,omitempty"`
DecryptedValue string `protobuf:"bytes,2,opt,name=decryptedValue,proto3" json:"decryptedValue,omitempty"`
Exists bool `protobuf:"varint,3,opt,name=exists,proto3" json:"exists,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetSecretResponse) Reset() {
*x = GetSecretResponse{}
mi := &file_secretsmanager_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetSecretResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetSecretResponse) ProtoMessage() {}
func (x *GetSecretResponse) ProtoReflect() protoreflect.Message {
mi := &file_secretsmanager_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetSecretResponse.ProtoReflect.Descriptor instead.
func (*GetSecretResponse) Descriptor() ([]byte, []int) {
return file_secretsmanager_proto_rawDescGZIP(), []int{3}
}
func (x *GetSecretResponse) GetUserFriendlyError() string {
if x != nil {
return x.UserFriendlyError
}
return ""
}
func (x *GetSecretResponse) GetDecryptedValue() string {
if x != nil {
return x.DecryptedValue
}
return ""
}
func (x *GetSecretResponse) GetExists() bool {
if x != nil {
return x.Exists
}
return false
}
type SetSecretRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
KeyDescriptor *Key `protobuf:"bytes,1,opt,name=keyDescriptor,proto3" json:"keyDescriptor,omitempty"`
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SetSecretRequest) Reset() {
*x = SetSecretRequest{}
mi := &file_secretsmanager_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SetSecretRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SetSecretRequest) ProtoMessage() {}
func (x *SetSecretRequest) ProtoReflect() protoreflect.Message {
mi := &file_secretsmanager_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SetSecretRequest.ProtoReflect.Descriptor instead.
func (*SetSecretRequest) Descriptor() ([]byte, []int) {
return file_secretsmanager_proto_rawDescGZIP(), []int{4}
}
func (x *SetSecretRequest) GetKeyDescriptor() *Key {
if x != nil {
return x.KeyDescriptor
}
return nil
}
func (x *SetSecretRequest) GetValue() string {
if x != nil {
return x.Value
}
return ""
}
type SetSecretResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserFriendlyError string `protobuf:"bytes,1,opt,name=userFriendlyError,proto3" json:"userFriendlyError,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SetSecretResponse) Reset() {
*x = SetSecretResponse{}
mi := &file_secretsmanager_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SetSecretResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SetSecretResponse) ProtoMessage() {}
func (x *SetSecretResponse) ProtoReflect() protoreflect.Message {
mi := &file_secretsmanager_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SetSecretResponse.ProtoReflect.Descriptor instead.
func (*SetSecretResponse) Descriptor() ([]byte, []int) {
return file_secretsmanager_proto_rawDescGZIP(), []int{5}
}
func (x *SetSecretResponse) GetUserFriendlyError() string {
if x != nil {
return x.UserFriendlyError
}
return ""
}
type DeleteSecretRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
KeyDescriptor *Key `protobuf:"bytes,1,opt,name=keyDescriptor,proto3" json:"keyDescriptor,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *DeleteSecretRequest) Reset() {
*x = DeleteSecretRequest{}
mi := &file_secretsmanager_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *DeleteSecretRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeleteSecretRequest) ProtoMessage() {}
func (x *DeleteSecretRequest) ProtoReflect() protoreflect.Message {
mi := &file_secretsmanager_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DeleteSecretRequest.ProtoReflect.Descriptor instead.
func (*DeleteSecretRequest) Descriptor() ([]byte, []int) {
return file_secretsmanager_proto_rawDescGZIP(), []int{6}
}
func (x *DeleteSecretRequest) GetKeyDescriptor() *Key {
if x != nil {
return x.KeyDescriptor
}
return nil
}
type DeleteSecretResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserFriendlyError string `protobuf:"bytes,1,opt,name=userFriendlyError,proto3" json:"userFriendlyError,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *DeleteSecretResponse) Reset() {
*x = DeleteSecretResponse{}
mi := &file_secretsmanager_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *DeleteSecretResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeleteSecretResponse) ProtoMessage() {}
func (x *DeleteSecretResponse) ProtoReflect() protoreflect.Message {
mi := &file_secretsmanager_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DeleteSecretResponse.ProtoReflect.Descriptor instead.
func (*DeleteSecretResponse) Descriptor() ([]byte, []int) {
return file_secretsmanager_proto_rawDescGZIP(), []int{7}
}
func (x *DeleteSecretResponse) GetUserFriendlyError() string {
if x != nil {
return x.UserFriendlyError
}
return ""
}
type ListSecretsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
KeyDescriptor *Key `protobuf:"bytes,1,opt,name=keyDescriptor,proto3" json:"keyDescriptor,omitempty"`
AllOrganizations bool `protobuf:"varint,2,opt,name=allOrganizations,proto3" json:"allOrganizations,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListSecretsRequest) Reset() {
*x = ListSecretsRequest{}
mi := &file_secretsmanager_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListSecretsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListSecretsRequest) ProtoMessage() {}
func (x *ListSecretsRequest) ProtoReflect() protoreflect.Message {
mi := &file_secretsmanager_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListSecretsRequest.ProtoReflect.Descriptor instead.
func (*ListSecretsRequest) Descriptor() ([]byte, []int) {
return file_secretsmanager_proto_rawDescGZIP(), []int{8}
}
func (x *ListSecretsRequest) GetKeyDescriptor() *Key {
if x != nil {
return x.KeyDescriptor
}
return nil
}
func (x *ListSecretsRequest) GetAllOrganizations() bool {
if x != nil {
return x.AllOrganizations
}
return false
}
type ListSecretsResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserFriendlyError string `protobuf:"bytes,1,opt,name=userFriendlyError,proto3" json:"userFriendlyError,omitempty"`
Keys []*Key `protobuf:"bytes,2,rep,name=keys,proto3" json:"keys,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListSecretsResponse) Reset() {
*x = ListSecretsResponse{}
mi := &file_secretsmanager_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListSecretsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListSecretsResponse) ProtoMessage() {}
func (x *ListSecretsResponse) ProtoReflect() protoreflect.Message {
mi := &file_secretsmanager_proto_msgTypes[9]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListSecretsResponse.ProtoReflect.Descriptor instead.
func (*ListSecretsResponse) Descriptor() ([]byte, []int) {
return file_secretsmanager_proto_rawDescGZIP(), []int{9}
}
func (x *ListSecretsResponse) GetUserFriendlyError() string {
if x != nil {
return x.UserFriendlyError
}
return ""
}
func (x *ListSecretsResponse) GetKeys() []*Key {
if x != nil {
return x.Keys
}
return nil
}
type GetAllSecretsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetAllSecretsRequest) Reset() {
*x = GetAllSecretsRequest{}
mi := &file_secretsmanager_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetAllSecretsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetAllSecretsRequest) ProtoMessage() {}
func (x *GetAllSecretsRequest) ProtoReflect() protoreflect.Message {
mi := &file_secretsmanager_proto_msgTypes[10]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetAllSecretsRequest.ProtoReflect.Descriptor instead.
func (*GetAllSecretsRequest) Descriptor() ([]byte, []int) {
return file_secretsmanager_proto_rawDescGZIP(), []int{10}
}
type GetAllSecretsResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserFriendlyError string `protobuf:"bytes,1,opt,name=userFriendlyError,proto3" json:"userFriendlyError,omitempty"`
Items []*Item `protobuf:"bytes,2,rep,name=items,proto3" json:"items,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetAllSecretsResponse) Reset() {
*x = GetAllSecretsResponse{}
mi := &file_secretsmanager_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetAllSecretsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetAllSecretsResponse) ProtoMessage() {}
func (x *GetAllSecretsResponse) ProtoReflect() protoreflect.Message {
mi := &file_secretsmanager_proto_msgTypes[11]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetAllSecretsResponse.ProtoReflect.Descriptor instead.
func (*GetAllSecretsResponse) Descriptor() ([]byte, []int) {
return file_secretsmanager_proto_rawDescGZIP(), []int{11}
}
func (x *GetAllSecretsResponse) GetUserFriendlyError() string {
if x != nil {
return x.UserFriendlyError
}
return ""
}
func (x *GetAllSecretsResponse) GetItems() []*Item {
if x != nil {
return x.Items
}
return nil
}
type RenameSecretRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
KeyDescriptor *Key `protobuf:"bytes,1,opt,name=keyDescriptor,proto3" json:"keyDescriptor,omitempty"`
NewNamespace string `protobuf:"bytes,2,opt,name=newNamespace,proto3" json:"newNamespace,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RenameSecretRequest) Reset() {
*x = RenameSecretRequest{}
mi := &file_secretsmanager_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RenameSecretRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RenameSecretRequest) ProtoMessage() {}
func (x *RenameSecretRequest) ProtoReflect() protoreflect.Message {
mi := &file_secretsmanager_proto_msgTypes[12]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RenameSecretRequest.ProtoReflect.Descriptor instead.
func (*RenameSecretRequest) Descriptor() ([]byte, []int) {
return file_secretsmanager_proto_rawDescGZIP(), []int{12}
}
func (x *RenameSecretRequest) GetKeyDescriptor() *Key {
if x != nil {
return x.KeyDescriptor
}
return nil
}
func (x *RenameSecretRequest) GetNewNamespace() string {
if x != nil {
return x.NewNamespace
}
return ""
}
type RenameSecretResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserFriendlyError string `protobuf:"bytes,1,opt,name=userFriendlyError,proto3" json:"userFriendlyError,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RenameSecretResponse) Reset() {
*x = RenameSecretResponse{}
mi := &file_secretsmanager_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RenameSecretResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RenameSecretResponse) ProtoMessage() {}
func (x *RenameSecretResponse) ProtoReflect() protoreflect.Message {
mi := &file_secretsmanager_proto_msgTypes[13]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RenameSecretResponse.ProtoReflect.Descriptor instead.
func (*RenameSecretResponse) Descriptor() ([]byte, []int) {
return file_secretsmanager_proto_rawDescGZIP(), []int{13}
}
func (x *RenameSecretResponse) GetUserFriendlyError() string {
if x != nil {
return x.UserFriendlyError
}
return ""
}
var File_secretsmanager_proto protoreflect.FileDescriptor
var file_secretsmanager_proto_rawDesc = string([]byte{
0x0a, 0x14, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x14, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x6d,
0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x22, 0x4d, 0x0a, 0x03,
0x4b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01,
0x28, 0x03, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d,
0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61,
0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x49, 0x0a, 0x04, 0x49,
0x74, 0x65, 0x6d, 0x12, 0x2b, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x19, 0x2e, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x72, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79,
0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x53, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53, 0x65, 0x63,
0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x0d, 0x6b, 0x65,
0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x19, 0x2e, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67,
0x65, 0x72, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x0d, 0x6b, 0x65,
0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x22, 0x81, 0x01, 0x0a, 0x11,
0x47, 0x65, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x2c, 0x0a, 0x11, 0x75, 0x73, 0x65, 0x72, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x6c,
0x79, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x75, 0x73,
0x65, 0x72, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x6c, 0x79, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12,
0x26, 0x0a, 0x0e, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74,
0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x69, 0x73, 0x74,
0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x22,
0x69, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x0d, 0x6b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69,
0x70, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x73, 0x65, 0x63,
0x72, 0x65, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x70, 0x6c, 0x75, 0x67, 0x69,
0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x0d, 0x6b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69,
0x70, 0x74, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x41, 0x0a, 0x11, 0x53, 0x65,
0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x2c, 0x0a, 0x11, 0x75, 0x73, 0x65, 0x72, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x6c, 0x79, 0x45,
0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x75, 0x73, 0x65, 0x72,
0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x6c, 0x79, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x56, 0x0a,
0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x0d, 0x6b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72,
0x69, 0x70, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x73, 0x65,
0x63, 0x72, 0x65, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x70, 0x6c, 0x75, 0x67,
0x69, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x0d, 0x6b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72,
0x69, 0x70, 0x74, 0x6f, 0x72, 0x22, 0x44, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53,
0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a,
0x11, 0x75, 0x73, 0x65, 0x72, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x6c, 0x79, 0x45, 0x72, 0x72,
0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x75, 0x73, 0x65, 0x72, 0x46, 0x72,
0x69, 0x65, 0x6e, 0x64, 0x6c, 0x79, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x81, 0x01, 0x0a, 0x12,
0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x3f, 0x0a, 0x0d, 0x6b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x73, 0x65, 0x63, 0x72,
0x65, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e,
0x2e, 0x4b, 0x65, 0x79, 0x52, 0x0d, 0x6b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
0x74, 0x6f, 0x72, 0x12, 0x2a, 0x0a, 0x10, 0x61, 0x6c, 0x6c, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69,
0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x61,
0x6c, 0x6c, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22,
0x72, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x75, 0x73, 0x65, 0x72, 0x46, 0x72,
0x69, 0x65, 0x6e, 0x64, 0x6c, 0x79, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x11, 0x75, 0x73, 0x65, 0x72, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x6c, 0x79, 0x45,
0x72, 0x72, 0x6f, 0x72, 0x12, 0x2d, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x19, 0x2e, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61,
0x67, 0x65, 0x72, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x04, 0x6b,
0x65, 0x79, 0x73, 0x22, 0x16, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x53, 0x65, 0x63,
0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x77, 0x0a, 0x15, 0x47,
0x65, 0x74, 0x41, 0x6c, 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x75, 0x73, 0x65, 0x72, 0x46, 0x72, 0x69, 0x65,
0x6e, 0x64, 0x6c, 0x79, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x11, 0x75, 0x73, 0x65, 0x72, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x6c, 0x79, 0x45, 0x72, 0x72,
0x6f, 0x72, 0x12, 0x30, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x1a, 0x2e, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67,
0x65, 0x72, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69,
0x74, 0x65, 0x6d, 0x73, 0x22, 0x7a, 0x0a, 0x13, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65,
0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x0d, 0x6b,
0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x19, 0x2e, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61,
0x67, 0x65, 0x72, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x0d, 0x6b,
0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x22, 0x0a, 0x0c,
0x6e, 0x65, 0x77, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0c, 0x6e, 0x65, 0x77, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65,
0x22, 0x44, 0x0a, 0x14, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x75, 0x73, 0x65, 0x72,
0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x6c, 0x79, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x11, 0x75, 0x73, 0x65, 0x72, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x6c,
0x79, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x32, 0xe8, 0x04, 0x0a, 0x0e, 0x53, 0x65, 0x63, 0x72, 0x65,
0x74, 0x73, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x5c, 0x0a, 0x09, 0x47, 0x65, 0x74,
0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x26, 0x2e, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73,
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x47, 0x65,
0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27,
0x2e, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x70,
0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x09, 0x53, 0x65, 0x74, 0x53, 0x65,
0x63, 0x72, 0x65, 0x74, 0x12, 0x26, 0x2e, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x6d, 0x61,
0x6e, 0x61, 0x67, 0x65, 0x72, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x53,
0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x73,
0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x70, 0x6c, 0x75,
0x67, 0x69, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53,
0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x29, 0x2e, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x6d,
0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x44, 0x65, 0x6c,
0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x2a, 0x2e, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x72, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65,
0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0b,
0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x28, 0x2e, 0x73, 0x65,
0x63, 0x72, 0x65, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x70, 0x6c, 0x75, 0x67,
0x69, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x6d,
0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4c, 0x69, 0x73,
0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x65, 0x0a, 0x0c, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74,
0x12, 0x29, 0x2e, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x72, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65,
0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x73, 0x65,
0x63, 0x72, 0x65, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x70, 0x6c, 0x75, 0x67,
0x69, 0x6e, 0x2e, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x68, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x41, 0x6c,
0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x2a, 0x2e, 0x73, 0x65, 0x63, 0x72, 0x65,
0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e,
0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x6d, 0x61,
0x6e, 0x61, 0x67, 0x65, 0x72, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x41,
0x6c, 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x42, 0x19, 0x5a, 0x17, 0x2e, 0x2f, 0x3b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x6d,
0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
})
var (
file_secretsmanager_proto_rawDescOnce sync.Once
file_secretsmanager_proto_rawDescData []byte
)
func file_secretsmanager_proto_rawDescGZIP() []byte {
file_secretsmanager_proto_rawDescOnce.Do(func() {
file_secretsmanager_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_secretsmanager_proto_rawDesc), len(file_secretsmanager_proto_rawDesc)))
})
return file_secretsmanager_proto_rawDescData
}
var file_secretsmanager_proto_msgTypes = make([]protoimpl.MessageInfo, 14)
var file_secretsmanager_proto_goTypes = []any{
(*Key)(nil), // 0: secretsmanagerplugin.Key
(*Item)(nil), // 1: secretsmanagerplugin.Item
(*GetSecretRequest)(nil), // 2: secretsmanagerplugin.GetSecretRequest
(*GetSecretResponse)(nil), // 3: secretsmanagerplugin.GetSecretResponse
(*SetSecretRequest)(nil), // 4: secretsmanagerplugin.SetSecretRequest
(*SetSecretResponse)(nil), // 5: secretsmanagerplugin.SetSecretResponse
(*DeleteSecretRequest)(nil), // 6: secretsmanagerplugin.DeleteSecretRequest
(*DeleteSecretResponse)(nil), // 7: secretsmanagerplugin.DeleteSecretResponse
(*ListSecretsRequest)(nil), // 8: secretsmanagerplugin.ListSecretsRequest
(*ListSecretsResponse)(nil), // 9: secretsmanagerplugin.ListSecretsResponse
(*GetAllSecretsRequest)(nil), // 10: secretsmanagerplugin.GetAllSecretsRequest
(*GetAllSecretsResponse)(nil), // 11: secretsmanagerplugin.GetAllSecretsResponse
(*RenameSecretRequest)(nil), // 12: secretsmanagerplugin.RenameSecretRequest
(*RenameSecretResponse)(nil), // 13: secretsmanagerplugin.RenameSecretResponse
}
var file_secretsmanager_proto_depIdxs = []int32{
0, // 0: secretsmanagerplugin.Item.key:type_name -> secretsmanagerplugin.Key
0, // 1: secretsmanagerplugin.GetSecretRequest.keyDescriptor:type_name -> secretsmanagerplugin.Key
0, // 2: secretsmanagerplugin.SetSecretRequest.keyDescriptor:type_name -> secretsmanagerplugin.Key
0, // 3: secretsmanagerplugin.DeleteSecretRequest.keyDescriptor:type_name -> secretsmanagerplugin.Key
0, // 4: secretsmanagerplugin.ListSecretsRequest.keyDescriptor:type_name -> secretsmanagerplugin.Key
0, // 5: secretsmanagerplugin.ListSecretsResponse.keys:type_name -> secretsmanagerplugin.Key
1, // 6: secretsmanagerplugin.GetAllSecretsResponse.items:type_name -> secretsmanagerplugin.Item
0, // 7: secretsmanagerplugin.RenameSecretRequest.keyDescriptor:type_name -> secretsmanagerplugin.Key
2, // 8: secretsmanagerplugin.SecretsManager.GetSecret:input_type -> secretsmanagerplugin.GetSecretRequest
4, // 9: secretsmanagerplugin.SecretsManager.SetSecret:input_type -> secretsmanagerplugin.SetSecretRequest
6, // 10: secretsmanagerplugin.SecretsManager.DeleteSecret:input_type -> secretsmanagerplugin.DeleteSecretRequest
8, // 11: secretsmanagerplugin.SecretsManager.ListSecrets:input_type -> secretsmanagerplugin.ListSecretsRequest
12, // 12: secretsmanagerplugin.SecretsManager.RenameSecret:input_type -> secretsmanagerplugin.RenameSecretRequest
10, // 13: secretsmanagerplugin.SecretsManager.GetAllSecrets:input_type -> secretsmanagerplugin.GetAllSecretsRequest
3, // 14: secretsmanagerplugin.SecretsManager.GetSecret:output_type -> secretsmanagerplugin.GetSecretResponse
5, // 15: secretsmanagerplugin.SecretsManager.SetSecret:output_type -> secretsmanagerplugin.SetSecretResponse
7, // 16: secretsmanagerplugin.SecretsManager.DeleteSecret:output_type -> secretsmanagerplugin.DeleteSecretResponse
9, // 17: secretsmanagerplugin.SecretsManager.ListSecrets:output_type -> secretsmanagerplugin.ListSecretsResponse
13, // 18: secretsmanagerplugin.SecretsManager.RenameSecret:output_type -> secretsmanagerplugin.RenameSecretResponse
11, // 19: secretsmanagerplugin.SecretsManager.GetAllSecrets:output_type -> secretsmanagerplugin.GetAllSecretsResponse
14, // [14:20] is the sub-list for method output_type
8, // [8:14] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
}
func init() { file_secretsmanager_proto_init() }
func file_secretsmanager_proto_init() {
if File_secretsmanager_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_secretsmanager_proto_rawDesc), len(file_secretsmanager_proto_rawDesc)),
NumEnums: 0,
NumMessages: 14,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_secretsmanager_proto_goTypes,
DependencyIndexes: file_secretsmanager_proto_depIdxs,
MessageInfos: file_secretsmanager_proto_msgTypes,
}.Build()
File_secretsmanager_proto = out.File
file_secretsmanager_proto_goTypes = nil
file_secretsmanager_proto_depIdxs = nil
}
@@ -1,78 +0,0 @@
syntax = "proto3";
package secretsmanagerplugin;
option go_package = "./;secretsmanagerplugin";
message Key {
int64 orgId = 1;
string namespace = 2;
string type = 3;
}
message Item {
Key key = 1;
string value = 2;
}
message GetSecretRequest {
Key keyDescriptor = 1;
}
message GetSecretResponse {
string userFriendlyError = 1;
string decryptedValue = 2;
bool exists = 3;
}
message SetSecretRequest {
Key keyDescriptor = 1;
string value = 2;
}
message SetSecretResponse {
string userFriendlyError = 1;
}
message DeleteSecretRequest {
Key keyDescriptor = 1;
}
message DeleteSecretResponse {
string userFriendlyError = 1;
}
message ListSecretsRequest {
Key keyDescriptor = 1;
bool allOrganizations = 2;
}
message ListSecretsResponse {
string userFriendlyError = 1;
repeated Key keys = 2;
}
message GetAllSecretsRequest {
}
message GetAllSecretsResponse {
string userFriendlyError = 1;
repeated Item items = 2;
}
message RenameSecretRequest {
Key keyDescriptor = 1;
string newNamespace = 2;
}
message RenameSecretResponse {
string userFriendlyError = 1;
}
service SecretsManager {
rpc GetSecret(GetSecretRequest) returns (GetSecretResponse);
rpc SetSecret(SetSecretRequest) returns (SetSecretResponse);
rpc DeleteSecret(DeleteSecretRequest) returns (DeleteSecretResponse);
rpc ListSecrets(ListSecretsRequest) returns (ListSecretsResponse);
rpc RenameSecret(RenameSecretRequest) returns (RenameSecretResponse);
rpc GetAllSecrets(GetAllSecretsRequest) returns (GetAllSecretsResponse);
}
@@ -1,61 +0,0 @@
package secretsmanagerplugin
import (
"context"
"github.com/hashicorp/go-plugin"
"google.golang.org/grpc"
)
type SecretsManagerPlugin interface {
SecretsManagerClient
}
type SecretsManagerGRPCPlugin struct {
plugin.NetRPCUnsupportedPlugin
}
func (p *SecretsManagerGRPCPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
return nil
}
func (p *SecretsManagerGRPCPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (any, error) {
return &SecretsManagerGRPCClient{NewSecretsManagerClient(c)}, nil
}
type SecretsManagerGRPCClient struct {
SecretsManagerClient
}
// Get an item from the store
func (sm *SecretsManagerGRPCClient) GetSecret(ctx context.Context, req *GetSecretRequest, opts ...grpc.CallOption) (*GetSecretResponse, error) {
return sm.SecretsManagerClient.GetSecret(ctx, req)
}
// Set an item in the store
func (sm *SecretsManagerGRPCClient) SetSecret(ctx context.Context, req *SetSecretRequest, opts ...grpc.CallOption) (*SetSecretResponse, error) {
return sm.SecretsManagerClient.SetSecret(ctx, req)
}
// Del deletes an item from the store.
func (sm *SecretsManagerGRPCClient) DeleteSecret(ctx context.Context, req *DeleteSecretRequest, opts ...grpc.CallOption) (*DeleteSecretResponse, error) {
return sm.SecretsManagerClient.DeleteSecret(ctx, req)
}
// Keys get all keys for a given namespace.
func (sm *SecretsManagerGRPCClient) ListSecrets(ctx context.Context, req *ListSecretsRequest, opts ...grpc.CallOption) (*ListSecretsResponse, error) {
return sm.SecretsManagerClient.ListSecrets(ctx, req)
}
// Rename an item in the store
func (sm *SecretsManagerGRPCClient) RenameSecret(ctx context.Context, req *RenameSecretRequest, opts ...grpc.CallOption) (*RenameSecretResponse, error) {
return sm.SecretsManagerClient.RenameSecret(ctx, req)
}
// Get all items from the store
func (sm *SecretsManagerGRPCClient) GetAllSecrets(ctx context.Context, req *GetAllSecretsRequest, opts ...grpc.CallOption) (*GetAllSecretsResponse, error) {
return sm.SecretsManagerClient.GetAllSecrets(ctx, req)
}
var _ SecretsManagerClient = &SecretsManagerGRPCClient{}
var _ plugin.GRPCPlugin = &SecretsManagerGRPCPlugin{}
@@ -1,300 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.4.0
// - protoc (unknown)
// source: secretsmanager.proto
package secretsmanagerplugin
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.62.0 or later.
const _ = grpc.SupportPackageIsVersion8
const (
SecretsManager_GetSecret_FullMethodName = "/secretsmanagerplugin.SecretsManager/GetSecret"
SecretsManager_SetSecret_FullMethodName = "/secretsmanagerplugin.SecretsManager/SetSecret"
SecretsManager_DeleteSecret_FullMethodName = "/secretsmanagerplugin.SecretsManager/DeleteSecret"
SecretsManager_ListSecrets_FullMethodName = "/secretsmanagerplugin.SecretsManager/ListSecrets"
SecretsManager_RenameSecret_FullMethodName = "/secretsmanagerplugin.SecretsManager/RenameSecret"
SecretsManager_GetAllSecrets_FullMethodName = "/secretsmanagerplugin.SecretsManager/GetAllSecrets"
)
// SecretsManagerClient is the client API for SecretsManager service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type SecretsManagerClient interface {
GetSecret(ctx context.Context, in *GetSecretRequest, opts ...grpc.CallOption) (*GetSecretResponse, error)
SetSecret(ctx context.Context, in *SetSecretRequest, opts ...grpc.CallOption) (*SetSecretResponse, error)
DeleteSecret(ctx context.Context, in *DeleteSecretRequest, opts ...grpc.CallOption) (*DeleteSecretResponse, error)
ListSecrets(ctx context.Context, in *ListSecretsRequest, opts ...grpc.CallOption) (*ListSecretsResponse, error)
RenameSecret(ctx context.Context, in *RenameSecretRequest, opts ...grpc.CallOption) (*RenameSecretResponse, error)
GetAllSecrets(ctx context.Context, in *GetAllSecretsRequest, opts ...grpc.CallOption) (*GetAllSecretsResponse, error)
}
type secretsManagerClient struct {
cc grpc.ClientConnInterface
}
func NewSecretsManagerClient(cc grpc.ClientConnInterface) SecretsManagerClient {
return &secretsManagerClient{cc}
}
func (c *secretsManagerClient) GetSecret(ctx context.Context, in *GetSecretRequest, opts ...grpc.CallOption) (*GetSecretResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetSecretResponse)
err := c.cc.Invoke(ctx, SecretsManager_GetSecret_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *secretsManagerClient) SetSecret(ctx context.Context, in *SetSecretRequest, opts ...grpc.CallOption) (*SetSecretResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SetSecretResponse)
err := c.cc.Invoke(ctx, SecretsManager_SetSecret_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *secretsManagerClient) DeleteSecret(ctx context.Context, in *DeleteSecretRequest, opts ...grpc.CallOption) (*DeleteSecretResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeleteSecretResponse)
err := c.cc.Invoke(ctx, SecretsManager_DeleteSecret_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *secretsManagerClient) ListSecrets(ctx context.Context, in *ListSecretsRequest, opts ...grpc.CallOption) (*ListSecretsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListSecretsResponse)
err := c.cc.Invoke(ctx, SecretsManager_ListSecrets_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *secretsManagerClient) RenameSecret(ctx context.Context, in *RenameSecretRequest, opts ...grpc.CallOption) (*RenameSecretResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RenameSecretResponse)
err := c.cc.Invoke(ctx, SecretsManager_RenameSecret_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *secretsManagerClient) GetAllSecrets(ctx context.Context, in *GetAllSecretsRequest, opts ...grpc.CallOption) (*GetAllSecretsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetAllSecretsResponse)
err := c.cc.Invoke(ctx, SecretsManager_GetAllSecrets_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// SecretsManagerServer is the server API for SecretsManager service.
// All implementations must embed UnimplementedSecretsManagerServer
// for forward compatibility
type SecretsManagerServer interface {
GetSecret(context.Context, *GetSecretRequest) (*GetSecretResponse, error)
SetSecret(context.Context, *SetSecretRequest) (*SetSecretResponse, error)
DeleteSecret(context.Context, *DeleteSecretRequest) (*DeleteSecretResponse, error)
ListSecrets(context.Context, *ListSecretsRequest) (*ListSecretsResponse, error)
RenameSecret(context.Context, *RenameSecretRequest) (*RenameSecretResponse, error)
GetAllSecrets(context.Context, *GetAllSecretsRequest) (*GetAllSecretsResponse, error)
mustEmbedUnimplementedSecretsManagerServer()
}
// UnimplementedSecretsManagerServer must be embedded to have forward compatible implementations.
type UnimplementedSecretsManagerServer struct {
}
func (UnimplementedSecretsManagerServer) GetSecret(context.Context, *GetSecretRequest) (*GetSecretResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetSecret not implemented")
}
func (UnimplementedSecretsManagerServer) SetSecret(context.Context, *SetSecretRequest) (*SetSecretResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetSecret not implemented")
}
func (UnimplementedSecretsManagerServer) DeleteSecret(context.Context, *DeleteSecretRequest) (*DeleteSecretResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteSecret not implemented")
}
func (UnimplementedSecretsManagerServer) ListSecrets(context.Context, *ListSecretsRequest) (*ListSecretsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListSecrets not implemented")
}
func (UnimplementedSecretsManagerServer) RenameSecret(context.Context, *RenameSecretRequest) (*RenameSecretResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RenameSecret not implemented")
}
func (UnimplementedSecretsManagerServer) GetAllSecrets(context.Context, *GetAllSecretsRequest) (*GetAllSecretsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetAllSecrets not implemented")
}
func (UnimplementedSecretsManagerServer) mustEmbedUnimplementedSecretsManagerServer() {}
// UnsafeSecretsManagerServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to SecretsManagerServer will
// result in compilation errors.
type UnsafeSecretsManagerServer interface {
mustEmbedUnimplementedSecretsManagerServer()
}
func RegisterSecretsManagerServer(s grpc.ServiceRegistrar, srv SecretsManagerServer) {
s.RegisterService(&SecretsManager_ServiceDesc, srv)
}
func _SecretsManager_GetSecret_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetSecretRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SecretsManagerServer).GetSecret(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SecretsManager_GetSecret_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SecretsManagerServer).GetSecret(ctx, req.(*GetSecretRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SecretsManager_SetSecret_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SetSecretRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SecretsManagerServer).SetSecret(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SecretsManager_SetSecret_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SecretsManagerServer).SetSecret(ctx, req.(*SetSecretRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SecretsManager_DeleteSecret_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteSecretRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SecretsManagerServer).DeleteSecret(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SecretsManager_DeleteSecret_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SecretsManagerServer).DeleteSecret(ctx, req.(*DeleteSecretRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SecretsManager_ListSecrets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListSecretsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SecretsManagerServer).ListSecrets(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SecretsManager_ListSecrets_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SecretsManagerServer).ListSecrets(ctx, req.(*ListSecretsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SecretsManager_RenameSecret_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RenameSecretRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SecretsManagerServer).RenameSecret(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SecretsManager_RenameSecret_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SecretsManagerServer).RenameSecret(ctx, req.(*RenameSecretRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SecretsManager_GetAllSecrets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetAllSecretsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SecretsManagerServer).GetAllSecrets(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SecretsManager_GetAllSecrets_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SecretsManagerServer).GetAllSecrets(ctx, req.(*GetAllSecretsRequest))
}
return interceptor(ctx, in, info, handler)
}
// SecretsManager_ServiceDesc is the grpc.ServiceDesc for SecretsManager service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var SecretsManager_ServiceDesc = grpc.ServiceDesc{
ServiceName: "secretsmanagerplugin.SecretsManager",
HandlerType: (*SecretsManagerServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetSecret",
Handler: _SecretsManager_GetSecret_Handler,
},
{
MethodName: "SetSecret",
Handler: _SecretsManager_SetSecret_Handler,
},
{
MethodName: "DeleteSecret",
Handler: _SecretsManager_DeleteSecret_Handler,
},
{
MethodName: "ListSecrets",
Handler: _SecretsManager_ListSecrets_Handler,
},
{
MethodName: "RenameSecret",
Handler: _SecretsManager_RenameSecret_Handler,
},
{
MethodName: "GetAllSecrets",
Handler: _SecretsManager_GetAllSecrets_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "secretsmanager.proto",
}
-5
View File
@@ -98,11 +98,6 @@ type BackendFactoryProvider interface {
BackendFactory(ctx context.Context, p *Plugin) backendplugin.PluginFactoryFunc
}
type SecretsPluginManager interface {
// SecretsManager returns a secretsmanager plugin
SecretsManager(ctx context.Context) *Plugin
}
type StaticRouteResolver interface {
Routes(ctx context.Context) []*StaticRoute
}
@@ -90,6 +90,7 @@ func TestLoader_Load(t *testing.T) {
},
Links: []plugins.InfoLink{
{Name: "Raise issue", URL: "https://github.com/grafana/grafana/issues/new"},
{Name: "Documentation", URL: "https://grafana.com/docs/grafana/latest/datasources/aws-cloudwatch/"},
},
},
Includes: []*plugins.Includes{
@@ -64,30 +64,6 @@ func TestInitializer_Initialize(t *testing.T) {
require.NotNil(t, c)
})
t.Run("secretsmanager", func(t *testing.T) {
p := &plugins.Plugin{
JSONData: plugins.JSONData{
ID: "test",
Type: plugins.TypeSecretsManager,
Dependencies: plugins.Dependencies{
GrafanaVersion: ">=8.x",
},
Backend: true,
},
Class: plugins.ClassExternal,
}
stepFunc := BackendClientInitStep(&fakeEnvVarsProvider{}, &fakeBackendProvider{plugin: p}, fakes.InitializeNoopTracerForTest())
var err error
p, err = stepFunc(context.Background(), p)
require.NoError(t, err)
c, exists := p.Client()
require.True(t, exists)
require.NotNil(t, c)
})
t.Run("non backend plugin app", func(t *testing.T) {
p := &plugins.Plugin{
JSONData: plugins.JSONData{
+9 -21
View File
@@ -17,7 +17,6 @@ import (
"github.com/grafana/grafana/pkg/plugins/auth"
"github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
"github.com/grafana/grafana/pkg/plugins/backendplugin/secretsmanagerplugin"
"github.com/grafana/grafana/pkg/plugins/log"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/util"
@@ -58,10 +57,9 @@ type Plugin struct {
ExternalService *auth.ExternalService
Renderer pluginextensionv2.RendererPlugin
SecretsManager secretsmanagerplugin.SecretsManagerPlugin
client backendplugin.Plugin
log log.Logger
Renderer pluginextensionv2.RendererPlugin
client backendplugin.Plugin
log log.Logger
SkipHostEnvVars bool
@@ -126,7 +124,7 @@ type JSONData struct {
SDK bool `json:"sdk,omitempty"`
MultiValueFilterOperators bool `json:"multiValueFilterOperators,omitempty"`
// Backend (Datasource + Renderer + SecretsManager)
// Backend (Datasource + Renderer)
Executable string `json:"executable,omitempty"`
// App Service Auth Registration
@@ -442,10 +440,6 @@ func (p *Plugin) ExecutablePath() string {
return p.executablePath("plugin_start")
}
if p.IsSecretsManager() {
return p.executablePath("secrets_plugin_start")
}
return p.executablePath(p.Executable)
}
@@ -486,10 +480,6 @@ func (p *Plugin) IsRenderer() bool {
return p.Type == TypeRenderer
}
func (p *Plugin) IsSecretsManager() bool {
return p.Type == TypeSecretsManager
}
func (p *Plugin) IsApp() bool {
return p.Type == TypeApp
}
@@ -519,22 +509,20 @@ var PluginTypes = []Type{
TypePanel,
TypeApp,
TypeRenderer,
TypeSecretsManager,
}
type Type string
const (
TypeDataSource Type = "datasource"
TypePanel Type = "panel"
TypeApp Type = "app"
TypeRenderer Type = "renderer"
TypeSecretsManager Type = "secretsmanager"
TypeDataSource Type = "datasource"
TypePanel Type = "panel"
TypeApp Type = "app"
TypeRenderer Type = "renderer"
)
func (pt Type) IsValid() bool {
switch pt {
case TypeDataSource, TypePanel, TypeApp, TypeRenderer, TypeSecretsManager:
case TypeDataSource, TypePanel, TypeApp, TypeRenderer:
return true
}
return false
+30 -10
View File
@@ -2,7 +2,6 @@ package client
import (
"context"
"errors"
"fmt"
"net/http"
"sync"
@@ -13,9 +12,11 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/grafana/grafana/pkg/apimachinery/identity"
query "github.com/grafana/grafana/pkg/apis/query/v0alpha1"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/registry/apis/query/clientapi"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
@@ -26,6 +27,7 @@ import (
type pluginClient struct {
pluginClient plugins.Client
pCtxProvider *plugincontext.Provider
ac accesscontrol.AccessControl
}
type pluginRegistry struct {
@@ -43,10 +45,11 @@ var _ clientapi.QueryDataClient = (*pluginClient)(nil)
var _ query.DataSourceApiServerRegistry = (*pluginRegistry)(nil)
// NewQueryClientForPluginClient creates a client that delegates to the internal plugins.Client stack
func NewQueryClientForPluginClient(p plugins.Client, ctx *plugincontext.Provider) clientapi.QueryDataClient {
func NewQueryClientForPluginClient(p plugins.Client, ctx *plugincontext.Provider, accessControl accesscontrol.AccessControl) clientapi.QueryDataClient {
return &pluginClient{
pluginClient: p,
pCtxProvider: ctx,
ac: accessControl,
}
}
@@ -59,6 +62,17 @@ func NewDataSourceRegistryFromStore(pluginStore pluginstore.Store,
}
}
func (d *pluginClient) CanQueryDataSource(ctx context.Context, uid string) (bool, error) {
user, err := identity.GetRequester(ctx)
if err != nil {
return false, err
}
requiredScope := "datasources:uid:" + uid
evaluate := accesscontrol.EvalPermission(datasources.ActionQuery, requiredScope)
return d.ac.Evaluate(ctx, user, evaluate)
}
// ExecuteQueryData implements QueryHelper.
func (d *pluginClient) QueryData(ctx context.Context, req data.QueryDataRequest) (*backend.QueryDataResponse, error) {
queries, dsRef, err := data.ToDataSourceQueries(req)
@@ -69,17 +83,23 @@ func (d *pluginClient) QueryData(ctx context.Context, req data.QueryDataRequest)
return nil, fmt.Errorf("expected single datasource request")
}
canQuery, err := d.CanQueryDataSource(ctx, dsRef.UID)
if err != nil {
return nil, err
}
if !canQuery {
status := metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusForbidden,
Message: "Access denied to the data source",
}
return nil, &apierrors.StatusError{ErrStatus: status}
}
// NOTE: this depends on uid unique across datasources
settings, err := d.pCtxProvider.GetDataSourceInstanceSettings(ctx, dsRef.UID)
if err != nil {
if errors.Is(err, datasources.ErrDataSourceNotFound) {
status := metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusNotFound,
Message: "datasource not found",
}
return nil, &apierrors.StatusError{ErrStatus: status}
}
return nil, err
}
+1 -1
View File
@@ -127,7 +127,7 @@ func RegisterAPIService(features featuremgmt.FeatureToggles,
builder, err := NewQueryAPIBuilder(
features,
&CommonDataSourceClientSupplier{
Client: client.NewQueryClientForPluginClient(pluginClient, pCtxProvider),
Client: client.NewQueryClientForPluginClient(pluginClient, pCtxProvider, accessControl),
},
ar,
client.NewDataSourceRegistryFromStore(pluginStore, dataSourcesService),
+125
View File
@@ -0,0 +1,125 @@
package secret
import (
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/org"
)
const (
// SecureValues
ActionSecretSecureValuesCreate = "secret.securevalues:create" // CREATE.
ActionSecretSecureValuesWrite = "secret.securevalues:write" // UPDATE.
ActionSecretSecureValuesRead = "secret.securevalues:read" // GET + LIST.
ActionSecretSecureValuesDelete = "secret.securevalues:delete" // DELETE.
// Keepers
ActionSecretKeepersCreate = "secret.keepers:create" // CREATE.
ActionSecretKeepersWrite = "secret.keepers:write" // UPDATE.
ActionSecretKeepersRead = "secret.keepers:read" // GET + LIST.
ActionSecretKeepersDelete = "secret.keepers:delete" // DELETE.
)
var (
ScopeProviderSecretSecureValues = accesscontrol.NewScopeProvider("secret.securevalues")
ScopeProviderSecretKeepers = accesscontrol.NewScopeProvider("secret.keepers")
ScopeAllSecureValues = ScopeProviderSecretSecureValues.GetResourceAllScope()
ScopeAllKeepers = ScopeProviderSecretKeepers.GetResourceAllScope()
)
func RegisterAccessControlRoles(service accesscontrol.Service) error {
// SecureValues
secureValuesReader := accesscontrol.RoleRegistration{
Role: accesscontrol.RoleDTO{
Name: "fixed:secret.securevalues:reader",
DisplayName: "Secrets Manager secure values reader",
Description: "Read and list secure values.",
Group: "Secrets Manager",
Permissions: []accesscontrol.Permission{
{
Action: ActionSecretSecureValuesRead,
Scope: ScopeAllSecureValues,
},
},
},
Grants: []string{string(org.RoleAdmin)},
}
secureValuesWriter := accesscontrol.RoleRegistration{
Role: accesscontrol.RoleDTO{
Name: "fixed:secret.securevalues:writer",
DisplayName: "Secrets Manager secure values writer",
Description: "Create, update and delete secure values.",
Group: "Secrets Manager",
Permissions: []accesscontrol.Permission{
{
Action: ActionSecretSecureValuesCreate,
Scope: ScopeAllSecureValues,
},
{
Action: ActionSecretSecureValuesRead,
Scope: ScopeAllSecureValues,
},
{
Action: ActionSecretSecureValuesWrite,
Scope: ScopeAllSecureValues,
},
{
Action: ActionSecretSecureValuesDelete,
Scope: ScopeAllSecureValues,
},
},
},
Grants: []string{string(org.RoleAdmin)},
}
// Keepers
keepersReader := accesscontrol.RoleRegistration{
Role: accesscontrol.RoleDTO{
Name: "fixed:secret.keepers:reader",
DisplayName: "Secrets Manager keepers reader",
Description: "Read and list keepers.",
Group: "Secrets Manager",
Permissions: []accesscontrol.Permission{
{
Action: ActionSecretKeepersRead,
Scope: ScopeAllKeepers,
},
},
},
Grants: []string{string(org.RoleAdmin)},
}
keepersWriter := accesscontrol.RoleRegistration{
Role: accesscontrol.RoleDTO{
Name: "fixed:secret.keepers:writer",
DisplayName: "Secrets Manager keepers writer",
Description: "Create, update and delete keepers.",
Group: "Secrets Manager",
Permissions: []accesscontrol.Permission{
{
Action: ActionSecretKeepersCreate,
Scope: ScopeAllKeepers,
},
{
Action: ActionSecretKeepersRead,
Scope: ScopeAllKeepers,
},
{
Action: ActionSecretKeepersWrite,
Scope: ScopeAllKeepers,
},
{
Action: ActionSecretKeepersDelete,
Scope: ScopeAllKeepers,
},
},
},
Grants: []string{string(org.RoleAdmin)},
}
return service.DeclareFixedRoles(
secureValuesReader, secureValuesWriter,
keepersReader, keepersWriter,
)
}
@@ -2,12 +2,18 @@ package contracts
import (
"context"
"errors"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
)
var (
ErrKeeperNotFound = errors.New("keeper not found")
)
// KeeperMetadataStorage is the interface for wiring and dependency injection.
type KeeperMetadataStorage interface {
Create(ctx context.Context, keeper *secretv0alpha1.Keeper) (*secretv0alpha1.Keeper, error)
Read(ctx context.Context, namespace xkube.Namespace, name string) (*secretv0alpha1.Keeper, error)
@@ -2,12 +2,18 @@ package contracts
import (
"context"
"errors"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
)
var (
ErrSecureValueNotFound = errors.New("secure value not found")
)
// SecureValueMetadataStorage is the interface for wiring and dependency injection.
type SecureValueMetadataStorage interface {
Create(ctx context.Context, sv *secretv0alpha1.SecureValue) (*secretv0alpha1.SecureValue, error)
Read(ctx context.Context, namespace xkube.Namespace, name string) (*secretv0alpha1.SecureValue, error)
+6 -1
View File
@@ -21,6 +21,7 @@ import (
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/reststorage"
"github.com/grafana/grafana/pkg/services/accesscontrol"
authsvc "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer"
"github.com/grafana/grafana/pkg/services/apiserver/builder"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/setting"
@@ -68,6 +69,10 @@ func RegisterAPIService(
return nil, nil
}
if err := RegisterAccessControlRoles(accessControlService); err != nil {
return nil, fmt.Errorf("register secret access control roles: %w", err)
}
builder := NewSecretAPIBuilder(
tracer,
secureValueMetadataStorage,
@@ -148,7 +153,7 @@ func (b *SecretAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions
// For Secrets, this is not the case, but if we want to make it so, we need to update this ResourceAuthorizer to check the containing folder.
// If we ever want to do that, get guidance from IAM first as well.
func (b *SecretAPIBuilder) GetAuthorizer() authorizer.Authorizer {
return nil
return authsvc.NewResourceAuthorizer(b.accessClient)
}
// Register additional routes with the server.
@@ -1,128 +0,0 @@
package reststorage
import (
"context"
"fmt"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"github.com/grafana/grafana/pkg/apimachinery/utils"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
)
var (
_ rest.Scoper = (*KeeperRest)(nil)
_ rest.SingularNameProvider = (*KeeperRest)(nil)
_ rest.Getter = (*KeeperRest)(nil)
_ rest.Lister = (*KeeperRest)(nil)
_ rest.Storage = (*KeeperRest)(nil)
_ rest.Creater = (*KeeperRest)(nil)
_ rest.Updater = (*KeeperRest)(nil)
_ rest.GracefulDeleter = (*KeeperRest)(nil)
)
// KeeperRest is an ddimplementation of CRUDL operations on a `keeper` backed by TODO.
type KeeperRest struct {
storage contracts.KeeperMetadataStorage
resource utils.ResourceInfo
tableConverter rest.TableConvertor
}
// NewKeeperRest is a returns a constructed `*KeeperRest`.
func NewKeeperRest(storage contracts.KeeperMetadataStorage, resource utils.ResourceInfo) *KeeperRest {
return &KeeperRest{storage, resource, resource.TableConverter()}
}
// New returns an empty `*Keeper` that is used by the `Create` method.
func (s *KeeperRest) New() runtime.Object {
return s.resource.NewFunc()
}
// Destroy is called when? [TODO]
func (s *KeeperRest) Destroy() {}
// NamespaceScoped returns `true` because the storage is namespaced (== org).
func (s *KeeperRest) NamespaceScoped() bool {
return true
}
// GetSingularName is used by `kubectl` discovery to have singular name representation of resources.
func (s *KeeperRest) GetSingularName() string {
return s.resource.GetSingularName()
}
// NewList returns an empty `*KeeperList` that is used by the `List` method.
func (s *KeeperRest) NewList() runtime.Object {
return s.resource.NewListFunc()
}
// ConvertToTable is used by Kubernetes and converts objects to `metav1.Table`.
func (s *KeeperRest) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
return s.tableConverter.ConvertToTable(ctx, object, tableOptions)
}
// List calls the inner `store` (persistence) and returns a list of `Keepers` within a `namespace` filtered by the `options`.
func (s *KeeperRest) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
_, ok := request.NamespaceFrom(ctx)
if !ok {
return nil, fmt.Errorf("missing namespace")
}
return &secretv0alpha1.KeeperList{Items: make([]secretv0alpha1.Keeper, 0)}, nil
}
// Get calls the inner `store` (persistence) and returns a `Keeper` by `name`.
func (s *KeeperRest) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
_, ok := request.NamespaceFrom(ctx)
if !ok {
return nil, fmt.Errorf("missing namespace")
}
return nil, s.resource.NewNotFound(name)
}
// Create a new `Keeper`. Does some validation and allows empty `name` (generated).
func (s *KeeperRest) Create(
ctx context.Context,
obj runtime.Object,
createValidation rest.ValidateObjectFunc,
options *metav1.CreateOptions,
) (runtime.Object, error) {
return nil, nil
}
// Update a `Keeper`'s `value`. The second return parameter indicates whether the resource was newly created.
func (s *KeeperRest) Update(
ctx context.Context,
name string,
objInfo rest.UpdatedObjectInfo,
createValidation rest.ValidateObjectFunc,
updateValidation rest.ValidateObjectUpdateFunc,
forceAllowCreate bool,
options *metav1.UpdateOptions,
) (runtime.Object, bool, error) {
return nil, false, nil
}
// Delete calls the inner `store` (persistence) in order to delete the `Keeper`.
// The second return parameter `bool` indicates whether the delete was intant or not. It always is for `Keepers`.
func (s *KeeperRest) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
_, ok := request.NamespaceFrom(ctx)
if !ok {
return nil, false, fmt.Errorf("missing namespace")
}
return nil, true, nil
}
// ValidateKeeper does basic spec validation of a keeper.
func ValidateKeeper(keeper *secretv0alpha1.Keeper, operation admission.Operation) field.ErrorList {
return nil
}
@@ -0,0 +1,356 @@
package reststorage
import (
"context"
"errors"
"fmt"
"strings"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"github.com/grafana/grafana/pkg/apimachinery/utils"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
)
var (
_ rest.Scoper = (*KeeperRest)(nil)
_ rest.SingularNameProvider = (*KeeperRest)(nil)
_ rest.Getter = (*KeeperRest)(nil)
_ rest.Lister = (*KeeperRest)(nil)
_ rest.Storage = (*KeeperRest)(nil)
_ rest.Creater = (*KeeperRest)(nil)
_ rest.Updater = (*KeeperRest)(nil)
_ rest.GracefulDeleter = (*KeeperRest)(nil)
)
// KeeperRest is an implementation of CRUDL operations on a `keeper` backed by TODO.
type KeeperRest struct {
storage contracts.KeeperMetadataStorage
resource utils.ResourceInfo
tableConverter rest.TableConvertor
}
// NewKeeperRest is a returns a constructed `*KeeperRest`.
func NewKeeperRest(storage contracts.KeeperMetadataStorage, resource utils.ResourceInfo) *KeeperRest {
return &KeeperRest{storage, resource, resource.TableConverter()}
}
// New returns an empty `*Keeper` that is used by the `Create` method.
func (s *KeeperRest) New() runtime.Object {
return s.resource.NewFunc()
}
// Destroy is called when? [TODO]
func (s *KeeperRest) Destroy() {}
// NamespaceScoped returns `true` because the storage is namespaced (== org).
func (s *KeeperRest) NamespaceScoped() bool {
return true
}
// GetSingularName is used by `kubectl` discovery to have singular name representation of resources.
func (s *KeeperRest) GetSingularName() string {
return s.resource.GetSingularName()
}
// NewList returns an empty `*KeeperList` that is used by the `List` method.
func (s *KeeperRest) NewList() runtime.Object {
return s.resource.NewListFunc()
}
// ConvertToTable is used by Kubernetes and converts objects to `metav1.Table`.
func (s *KeeperRest) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
return s.tableConverter.ConvertToTable(ctx, object, tableOptions)
}
// List calls the inner `store` (persistence) and returns a list of `Keepers` within a `namespace` filtered by the `options`.
func (s *KeeperRest) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
namespace, ok := request.NamespaceFrom(ctx)
if !ok {
return nil, fmt.Errorf("missing namespace")
}
keepersList, err := s.storage.List(ctx, xkube.Namespace(namespace), options)
if err != nil {
return nil, fmt.Errorf("failed to list keepers: %w", err)
}
return keepersList, nil
}
// Get calls the inner `store` (persistence) and returns a `Keeper` by `name`.
func (s *KeeperRest) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
namespace, ok := request.NamespaceFrom(ctx)
if !ok {
return nil, fmt.Errorf("missing namespace")
}
kp, err := s.storage.Read(ctx, xkube.Namespace(namespace), name)
if err != nil {
if errors.Is(err, contracts.ErrKeeperNotFound) {
return nil, s.resource.NewNotFound(name)
}
return nil, fmt.Errorf("failed to read keeper: %w", err)
}
return kp, nil
}
// Create a new `Keeper`. Does some validation and allows empty `name` (generated).
func (s *KeeperRest) Create(
ctx context.Context,
obj runtime.Object,
createValidation rest.ValidateObjectFunc,
options *metav1.CreateOptions,
) (runtime.Object, error) {
kp, ok := obj.(*secretv0alpha1.Keeper)
if !ok {
return nil, fmt.Errorf("expected Keeper for create")
}
if err := createValidation(ctx, obj); err != nil {
return nil, err
}
createdKeeper, err := s.storage.Create(ctx, kp)
if err != nil {
var kErr xkube.ErrorLister
if errors.As(err, &kErr) {
return nil, apierrors.NewInvalid(kp.GroupVersionKind().GroupKind(), kp.Name, kErr.ErrorList())
}
return nil, fmt.Errorf("failed to create keeper: %w", err)
}
return createdKeeper, nil
}
// Update a `Keeper`'s `value`. The second return parameter indicates whether the resource was newly created.
func (s *KeeperRest) Update(
ctx context.Context,
name string,
objInfo rest.UpdatedObjectInfo,
createValidation rest.ValidateObjectFunc,
updateValidation rest.ValidateObjectUpdateFunc,
forceAllowCreate bool,
options *metav1.UpdateOptions,
) (runtime.Object, bool, error) {
oldObj, err := s.Get(ctx, name, &metav1.GetOptions{})
if err != nil {
return nil, false, err
}
// Makes sure the UID and ResourceVersion are OK.
// TODO: this also makes it so the labels and annotations are additive, unless we check and remove manually.
newObj, err := objInfo.UpdatedObject(ctx, oldObj)
if err != nil {
return nil, false, fmt.Errorf("k8s updated object: %w", err)
}
// The current supported behavior for `Update` is to replace the entire `spec` with the new one.
// Each provider-specific setting of a keeper lives at the top-level, so it makes it possible to change a provider
// during an update. Otherwise both old and new providers would be merged in the `newObj` which is not allowed.
if err := updateValidation(ctx, newObj, oldObj); err != nil {
return nil, false, err
}
newKeeper, ok := newObj.(*secretv0alpha1.Keeper)
if !ok {
return nil, false, fmt.Errorf("expected Keeper for update")
}
// TODO: do we need to do this here again? Probably not, but double-check!
newKeeper.Annotations = xkube.CleanAnnotations(newKeeper.Annotations)
// Current implementation replaces everything passed in the spec, so it is not a PATCH. Do we want/need to support that?
updatedKeeper, err := s.storage.Update(ctx, newKeeper)
if err != nil {
var kErr xkube.ErrorLister
if errors.As(err, &kErr) {
return nil, false, apierrors.NewInvalid(newKeeper.GroupVersionKind().GroupKind(), newKeeper.Name, kErr.ErrorList())
}
return nil, false, fmt.Errorf("failed to update keeper: %w", err)
}
return updatedKeeper, false, nil
}
// Delete calls the inner `store` (persistence) in order to delete the `Keeper`.
// The second return parameter `bool` indicates whether the delete was intant or not. It always is for `Keepers`.
func (s *KeeperRest) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
namespace, ok := request.NamespaceFrom(ctx)
if !ok {
return nil, false, fmt.Errorf("missing namespace")
}
if err := s.storage.Delete(ctx, xkube.Namespace(namespace), name); err != nil {
return nil, false, fmt.Errorf("failed to delete keeper: %w", err)
}
return nil, true, nil
}
// ValidateKeeper does basic spec validation of a keeper.
func ValidateKeeper(keeper *secretv0alpha1.Keeper, operation admission.Operation) field.ErrorList {
// Only validate Create and Update for now.
if operation != admission.Create && operation != admission.Update {
return nil
}
errs := make(field.ErrorList, 0)
if keeper.Spec.Title == "" {
errs = append(errs, field.Required(field.NewPath("spec", "title"), "a `title` is required"))
}
// Only one keeper type can be configured. Return early and don't validate the specific keeper fields.
if err := validateKeepers(keeper); err != nil {
errs = append(errs, err)
return errs
}
// TODO: Improve SQL keeper validation.
// SQL keeper is not allowed to use `secureValueName` in credentials fields to avoid depending on another keeper.
if keeper.IsSqlKeeper() {
if keeper.Spec.SQL.Encryption.AWS != nil {
if keeper.Spec.SQL.Encryption.AWS.AccessKeyID.SecureValueName != "" {
errs = append(errs, field.Forbidden(field.NewPath("spec", "aws", "accessKeyId"), "secureValueName cannot be used with SQL keeper"))
}
if keeper.Spec.SQL.Encryption.AWS.SecretAccessKey.SecureValueName != "" {
errs = append(errs, field.Forbidden(field.NewPath("spec", "aws", "secretAccessKey"), "secureValueName cannot be used with SQL keeper"))
}
}
if keeper.Spec.SQL.Encryption.Azure != nil && keeper.Spec.SQL.Encryption.Azure.ClientSecret.SecureValueName != "" {
errs = append(errs, field.Forbidden(field.NewPath("spec", "azure", "clientSecret"), "secureValueName cannot be used with SQL keeper"))
}
if keeper.Spec.SQL.Encryption.HashiCorp != nil && keeper.Spec.SQL.Encryption.HashiCorp.Token.SecureValueName != "" {
errs = append(errs, field.Forbidden(field.NewPath("spec", "hashicorp", "token"), "secureValueName cannot be used with SQL keeper"))
}
}
if keeper.Spec.AWS != nil {
if err := validateCredentialValue(field.NewPath("spec", "aws", "accessKeyId"), keeper.Spec.AWS.AccessKeyID); err != nil {
errs = append(errs, err)
}
if err := validateCredentialValue(field.NewPath("spec", "aws", "secretAccessKey"), keeper.Spec.AWS.SecretAccessKey); err != nil {
errs = append(errs, err)
}
}
if keeper.Spec.Azure != nil {
if keeper.Spec.Azure.KeyVaultName == "" {
errs = append(errs, field.Required(field.NewPath("spec", "azure", "keyVaultName"), "a `keyVaultName` is required"))
}
if keeper.Spec.Azure.TenantID == "" {
errs = append(errs, field.Required(field.NewPath("spec", "azure", "tenantId"), "a `tenantId` is required"))
}
if keeper.Spec.Azure.ClientID == "" {
errs = append(errs, field.Required(field.NewPath("spec", "azure", "clientId"), "a `clientId` is required"))
}
if err := validateCredentialValue(field.NewPath("spec", "azure", "clientSecret"), keeper.Spec.Azure.ClientSecret); err != nil {
errs = append(errs, err)
}
}
if keeper.Spec.GCP != nil {
if keeper.Spec.GCP.ProjectID == "" {
errs = append(errs, field.Required(field.NewPath("spec", "gcp", "projectId"), "a `projectId` is required"))
}
if keeper.Spec.GCP.CredentialsFile == "" {
errs = append(errs, field.Required(field.NewPath("spec", "gcp", "credentialsFile"), "a `credentialsFile` is required"))
}
}
if keeper.Spec.HashiCorp != nil {
if keeper.Spec.HashiCorp.Address == "" {
errs = append(errs, field.Required(field.NewPath("spec", "hashicorp", "address"), "a `address` is required"))
}
if err := validateCredentialValue(field.NewPath("spec", "hashicorp", "token"), keeper.Spec.HashiCorp.Token); err != nil {
errs = append(errs, err)
}
}
return errs
}
func validateKeepers(keeper *secretv0alpha1.Keeper) *field.Error {
availableKeepers := map[string]bool{
"sql": keeper.Spec.SQL != nil,
"aws": keeper.Spec.AWS != nil,
"azure": keeper.Spec.Azure != nil,
"gcp": keeper.Spec.GCP != nil,
"hashicorp": keeper.Spec.HashiCorp != nil,
}
configuredKeepers := make([]string, 0)
for keeperKind, notNil := range availableKeepers {
if notNil {
configuredKeepers = append(configuredKeepers, keeperKind)
}
}
if len(configuredKeepers) == 0 {
return field.Required(field.NewPath("spec"), "at least one `keeper` must be present")
}
if len(configuredKeepers) > 1 {
return field.Invalid(
field.NewPath("spec"),
strings.Join(configuredKeepers, " & "),
"only one `keeper` can be present at a time but found more",
)
}
return nil
}
func validateCredentialValue(path *field.Path, credentials secretv0alpha1.CredentialValue) *field.Error {
availableOptions := map[string]bool{
"secureValueName": credentials.SecureValueName != "",
"valueFromEnv": credentials.ValueFromEnv != "",
"valueFromConfig": credentials.ValueFromConfig != "",
}
configuredCredentials := make([]string, 0)
for credentialKind, notEmpty := range availableOptions {
if notEmpty {
configuredCredentials = append(configuredCredentials, credentialKind)
}
}
if len(configuredCredentials) == 0 {
return field.Required(path, "one of `secureValueName`, `valueFromEnv` or `valueFromConfig` must be present")
}
if len(configuredCredentials) > 1 {
return field.Invalid(
path,
strings.Join(configuredCredentials, " & "),
"only one of `secureValueName`, `valueFromEnv` or `valueFromConfig` must be present at a time but found more",
)
}
return nil
}
@@ -0,0 +1,332 @@
package reststorage
import (
"testing"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
"github.com/stretchr/testify/require"
"k8s.io/apiserver/pkg/admission"
)
func TestValidateKeeper(t *testing.T) {
t.Run("when creating a new keeper", func(t *testing.T) {
t.Run("the `title` must be present", func(t *testing.T) {
keeper := &secretv0alpha1.Keeper{
Spec: secretv0alpha1.KeeperSpec{
SQL: &secretv0alpha1.SQLKeeperConfig{},
},
}
errs := ValidateKeeper(keeper, admission.Create)
require.Len(t, errs, 1)
require.Equal(t, "spec.title", errs[0].Field)
})
})
t.Run("only one `keeper` must be present", func(t *testing.T) {
keeper := &secretv0alpha1.Keeper{
Spec: secretv0alpha1.KeeperSpec{
Title: "title",
SQL: &secretv0alpha1.SQLKeeperConfig{},
AWS: &secretv0alpha1.AWSKeeperConfig{},
Azure: &secretv0alpha1.AzureKeeperConfig{},
GCP: &secretv0alpha1.GCPKeeperConfig{},
HashiCorp: &secretv0alpha1.HashiCorpKeeperConfig{},
},
}
errs := ValidateKeeper(keeper, admission.Create)
require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field)
})
t.Run("at least one `keeper` must be present", func(t *testing.T) {
keeper := &secretv0alpha1.Keeper{
Spec: secretv0alpha1.KeeperSpec{
Title: "title",
},
}
errs := ValidateKeeper(keeper, admission.Create)
require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field)
})
t.Run("aws keeper validation", func(t *testing.T) {
validKeeperAWS := &secretv0alpha1.Keeper{
Spec: secretv0alpha1.KeeperSpec{
Title: "title",
AWS: &secretv0alpha1.AWSKeeperConfig{
AWSCredentials: secretv0alpha1.AWSCredentials{
AccessKeyID: secretv0alpha1.CredentialValue{
ValueFromEnv: "some-value",
},
SecretAccessKey: secretv0alpha1.CredentialValue{
SecureValueName: "some-value",
},
KMSKeyID: "optional",
},
},
},
}
t.Run("`accessKeyId` must be present", func(t *testing.T) {
t.Run("at least one of the credential value must be present", func(t *testing.T) {
keeper := validKeeperAWS.DeepCopy()
keeper.Spec.AWS.AccessKeyID = secretv0alpha1.CredentialValue{}
errs := ValidateKeeper(keeper, admission.Create)
require.Len(t, errs, 1)
require.Equal(t, "spec.aws.accessKeyId", errs[0].Field)
})
t.Run("at most one of the credential value must be present", func(t *testing.T) {
keeper := validKeeperAWS.DeepCopy()
keeper.Spec.AWS.AccessKeyID = secretv0alpha1.CredentialValue{
SecureValueName: "a",
ValueFromEnv: "b",
ValueFromConfig: "c",
}
errs := ValidateKeeper(keeper, admission.Create)
require.Len(t, errs, 1)
require.Equal(t, "spec.aws.accessKeyId", errs[0].Field)
})
})
t.Run("`secretAccessKey` must be present", func(t *testing.T) {
t.Run("at least one of the credential value must be present", func(t *testing.T) {
keeper := validKeeperAWS.DeepCopy()
keeper.Spec.AWS.SecretAccessKey = secretv0alpha1.CredentialValue{}
errs := ValidateKeeper(keeper, admission.Create)
require.Len(t, errs, 1)
require.Equal(t, "spec.aws.secretAccessKey", errs[0].Field)
})
t.Run("at most one of the credential value must be present", func(t *testing.T) {
keeper := validKeeperAWS.DeepCopy()
keeper.Spec.AWS.SecretAccessKey = secretv0alpha1.CredentialValue{
SecureValueName: "a",
ValueFromEnv: "b",
ValueFromConfig: "c",
}
errs := ValidateKeeper(keeper, admission.Create)
require.Len(t, errs, 1)
require.Equal(t, "spec.aws.secretAccessKey", errs[0].Field)
})
})
})
t.Run("azure keeper validation", func(t *testing.T) {
validKeeperAzure := &secretv0alpha1.Keeper{
Spec: secretv0alpha1.KeeperSpec{
Title: "title",
Azure: &secretv0alpha1.AzureKeeperConfig{
AzureCredentials: secretv0alpha1.AzureCredentials{
KeyVaultName: "kv-name",
TenantID: "tenant-id",
ClientID: "client-id",
ClientSecret: secretv0alpha1.CredentialValue{
ValueFromConfig: "config.path.value",
},
},
},
},
}
t.Run("`keyVaultName` must be present", func(t *testing.T) {
keeper := validKeeperAzure.DeepCopy()
keeper.Spec.Azure.KeyVaultName = ""
errs := ValidateKeeper(keeper, admission.Create)
require.Len(t, errs, 1)
require.Equal(t, "spec.azure.keyVaultName", errs[0].Field)
})
t.Run("`tenantId` must be present", func(t *testing.T) {
keeper := validKeeperAzure.DeepCopy()
keeper.Spec.Azure.TenantID = ""
errs := ValidateKeeper(keeper, admission.Create)
require.Len(t, errs, 1)
require.Equal(t, "spec.azure.tenantId", errs[0].Field)
})
t.Run("`clientId` must be present", func(t *testing.T) {
keeper := validKeeperAzure.DeepCopy()
keeper.Spec.Azure.ClientID = ""
errs := ValidateKeeper(keeper, admission.Create)
require.Len(t, errs, 1)
require.Equal(t, "spec.azure.clientId", errs[0].Field)
})
t.Run("`clientSecret` must be present", func(t *testing.T) {
t.Run("at least one of the credential value must be present", func(t *testing.T) {
keeper := validKeeperAzure.DeepCopy()
keeper.Spec.Azure.ClientSecret = secretv0alpha1.CredentialValue{}
errs := ValidateKeeper(keeper, admission.Create)
require.Len(t, errs, 1)
require.Equal(t, "spec.azure.clientSecret", errs[0].Field)
})
t.Run("at most one of the credential value must be present", func(t *testing.T) {
keeper := validKeeperAzure.DeepCopy()
keeper.Spec.Azure.ClientSecret = secretv0alpha1.CredentialValue{
SecureValueName: "a",
ValueFromEnv: "b",
ValueFromConfig: "c",
}
errs := ValidateKeeper(keeper, admission.Create)
require.Len(t, errs, 1)
require.Equal(t, "spec.azure.clientSecret", errs[0].Field)
})
})
})
t.Run("gcp keeper validation", func(t *testing.T) {
validKeeperGCP := &secretv0alpha1.Keeper{
Spec: secretv0alpha1.KeeperSpec{
Title: "title",
GCP: &secretv0alpha1.GCPKeeperConfig{
GCPCredentials: secretv0alpha1.GCPCredentials{
ProjectID: "project-id",
CredentialsFile: "/path/to/credentials/file.json",
},
},
},
}
t.Run("`projectId` must be present", func(t *testing.T) {
keeper := validKeeperGCP.DeepCopy()
keeper.Spec.GCP.ProjectID = ""
errs := ValidateKeeper(keeper, admission.Create)
require.Len(t, errs, 1)
require.Equal(t, "spec.gcp.projectId", errs[0].Field)
})
t.Run("`credentialsFile` must be present", func(t *testing.T) {
keeper := validKeeperGCP.DeepCopy()
keeper.Spec.GCP.CredentialsFile = ""
errs := ValidateKeeper(keeper, admission.Create)
require.Len(t, errs, 1)
require.Equal(t, "spec.gcp.credentialsFile", errs[0].Field)
})
})
t.Run("hashicorp keeper validation", func(t *testing.T) {
validKeeperHashiCorp := &secretv0alpha1.Keeper{
Spec: secretv0alpha1.KeeperSpec{
Title: "title",
HashiCorp: &secretv0alpha1.HashiCorpKeeperConfig{
HashiCorpCredentials: secretv0alpha1.HashiCorpCredentials{
Address: "http://address",
Token: secretv0alpha1.CredentialValue{
ValueFromConfig: "config.path.value",
},
},
},
},
}
t.Run("`address` must be present", func(t *testing.T) {
keeper := validKeeperHashiCorp.DeepCopy()
keeper.Spec.HashiCorp.Address = ""
errs := ValidateKeeper(keeper, admission.Create)
require.Len(t, errs, 1)
require.Equal(t, "spec.hashicorp.address", errs[0].Field)
})
t.Run("`token` must be present", func(t *testing.T) {
t.Run("at least one of the credential value must be present", func(t *testing.T) {
keeper := validKeeperHashiCorp.DeepCopy()
keeper.Spec.HashiCorp.Token = secretv0alpha1.CredentialValue{}
errs := ValidateKeeper(keeper, admission.Create)
require.Len(t, errs, 1)
require.Equal(t, "spec.hashicorp.token", errs[0].Field)
})
t.Run("at most one of the credential value must be present", func(t *testing.T) {
keeper := validKeeperHashiCorp.DeepCopy()
keeper.Spec.HashiCorp.Token = secretv0alpha1.CredentialValue{
SecureValueName: "a",
ValueFromEnv: "b",
ValueFromConfig: "c",
}
errs := ValidateKeeper(keeper, admission.Create)
require.Len(t, errs, 1)
require.Equal(t, "spec.hashicorp.token", errs[0].Field)
})
})
})
t.Run("sql keeper validation", func(t *testing.T) {
t.Run("does not allow usage of `secureValueName` in credentials", func(t *testing.T) {
providers := []struct {
name string
enc secretv0alpha1.Encryption
expectedErrors int
}{
{
name: "aws",
enc: secretv0alpha1.Encryption{
AWS: &secretv0alpha1.AWSCredentials{
AccessKeyID: secretv0alpha1.CredentialValue{
SecureValueName: "not-empty",
},
SecretAccessKey: secretv0alpha1.CredentialValue{
SecureValueName: "not-empty",
},
},
},
expectedErrors: 2,
},
{
name: "azure",
enc: secretv0alpha1.Encryption{
Azure: &secretv0alpha1.AzureCredentials{
ClientSecret: secretv0alpha1.CredentialValue{
SecureValueName: "not-empty",
},
},
},
expectedErrors: 1,
},
{
name: "hashicorp",
enc: secretv0alpha1.Encryption{
HashiCorp: &secretv0alpha1.HashiCorpCredentials{
Token: secretv0alpha1.CredentialValue{
SecureValueName: "not-empty",
},
},
},
expectedErrors: 1,
},
}
for _, tc := range providers {
t.Run("when using credentials for "+tc.name, func(t *testing.T) {
keeper := &secretv0alpha1.Keeper{
Spec: secretv0alpha1.KeeperSpec{
Title: "title",
SQL: &secretv0alpha1.SQLKeeperConfig{Encryption: &tc.enc},
},
}
errs := ValidateKeeper(keeper, admission.Create)
require.Len(t, errs, tc.expectedErrors)
})
}
})
})
}
@@ -1,129 +0,0 @@
package reststorage
import (
"context"
"fmt"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"github.com/grafana/grafana/pkg/apimachinery/utils"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
)
var (
_ rest.Scoper = (*SecureValueRest)(nil)
_ rest.SingularNameProvider = (*SecureValueRest)(nil)
_ rest.Getter = (*SecureValueRest)(nil)
_ rest.Lister = (*SecureValueRest)(nil)
_ rest.Storage = (*SecureValueRest)(nil)
_ rest.Creater = (*SecureValueRest)(nil)
_ rest.Updater = (*SecureValueRest)(nil)
_ rest.GracefulDeleter = (*SecureValueRest)(nil)
)
// SecureValueRest is an implementation of CRUDL operations on a `securevalue` backed by a persistence layer `store`.
type SecureValueRest struct {
storage contracts.SecureValueMetadataStorage
resource utils.ResourceInfo
tableConverter rest.TableConvertor
}
// NewSecureValueRest is a returns a constructed `*SecureValueRest`.
func NewSecureValueRest(storage contracts.SecureValueMetadataStorage, resource utils.ResourceInfo) *SecureValueRest {
return &SecureValueRest{storage, resource, resource.TableConverter()}
}
// New returns an empty `*SecureValue` that is used by the `Create` method.
func (s *SecureValueRest) New() runtime.Object {
return s.resource.NewFunc()
}
// Destroy is called when? [TODO]
func (s *SecureValueRest) Destroy() {}
// NamespaceScoped returns `true` because the storage is namespaced (== org).
func (s *SecureValueRest) NamespaceScoped() bool {
return true
}
// GetSingularName is used by `kubectl` discovery to have singular name representation of resources.
func (s *SecureValueRest) GetSingularName() string {
return s.resource.GetSingularName()
}
// NewList returns an empty `*SecureValueList` that is used by the `List` method.
func (s *SecureValueRest) NewList() runtime.Object {
return s.resource.NewListFunc()
}
// ConvertToTable is used by Kubernetes and converts objects to `metav1.Table`.
func (s *SecureValueRest) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
return s.tableConverter.ConvertToTable(ctx, object, tableOptions)
}
// List calls the inner `store` (persistence) and returns a list of `securevalues` within a `namespace` filtered by the `options`.
func (s *SecureValueRest) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
_, ok := request.NamespaceFrom(ctx)
if !ok {
return nil, fmt.Errorf("missing namespace")
}
return &secretv0alpha1.SecureValueList{Items: make([]secretv0alpha1.SecureValue, 0)}, nil
}
// Get calls the inner `store` (persistence) and returns a `securevalue` by `name`. It will NOT return the decrypted `value`.
func (s *SecureValueRest) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
_, ok := request.NamespaceFrom(ctx)
if !ok {
return nil, fmt.Errorf("missing namespace")
}
return nil, s.resource.NewNotFound(name)
}
// Create a new `securevalue`. Does some validation and allows empty `name` (generated).
func (s *SecureValueRest) Create(
ctx context.Context,
obj runtime.Object,
createValidation rest.ValidateObjectFunc,
options *metav1.CreateOptions,
) (runtime.Object, error) {
return nil, nil
}
// Update a `securevalue`'s `value`. The second return parameter indicates whether the resource was newly created.
// Currently does not support "create on update" functionality. If the securevalue does not yet exist, it returns an error.
func (s *SecureValueRest) Update(
ctx context.Context,
name string,
objInfo rest.UpdatedObjectInfo,
createValidation rest.ValidateObjectFunc,
updateValidation rest.ValidateObjectUpdateFunc,
forceAllowCreate bool,
options *metav1.UpdateOptions,
) (runtime.Object, bool, error) {
return nil, false, nil
}
// Delete calls the inner `store` (persistence) in order to delete the `securevalue`.
// The second return parameter `bool` indicates whether the delete was instant or not. It always is for `securevalues`.
func (s *SecureValueRest) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
_, ok := request.NamespaceFrom(ctx)
if !ok {
return nil, false, fmt.Errorf("missing namespace")
}
return nil, true, nil
}
// ValidateSecureValue does basic spec validation of a securevalue.
func ValidateSecureValue(sv, oldSv *secretv0alpha1.SecureValue, operation admission.Operation, decryptersAllowList map[string]struct{}) field.ErrorList {
return nil
}
@@ -0,0 +1,315 @@
package reststorage
import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"github.com/grafana/grafana/pkg/apimachinery/utils"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
)
var (
_ rest.Scoper = (*SecureValueRest)(nil)
_ rest.SingularNameProvider = (*SecureValueRest)(nil)
_ rest.Getter = (*SecureValueRest)(nil)
_ rest.Lister = (*SecureValueRest)(nil)
_ rest.Storage = (*SecureValueRest)(nil)
_ rest.Creater = (*SecureValueRest)(nil)
_ rest.Updater = (*SecureValueRest)(nil)
_ rest.GracefulDeleter = (*SecureValueRest)(nil)
)
// SecureValueRest is an implementation of CRUDL operations on a `securevalue` backed by a persistence layer `store`.
type SecureValueRest struct {
storage contracts.SecureValueMetadataStorage
resource utils.ResourceInfo
tableConverter rest.TableConvertor
}
// NewSecureValueRest is a returns a constructed `*SecureValueRest`.
func NewSecureValueRest(storage contracts.SecureValueMetadataStorage, resource utils.ResourceInfo) *SecureValueRest {
return &SecureValueRest{storage, resource, resource.TableConverter()}
}
// New returns an empty `*SecureValue` that is used by the `Create` method.
func (s *SecureValueRest) New() runtime.Object {
return s.resource.NewFunc()
}
// Destroy is called when? [TODO]
func (s *SecureValueRest) Destroy() {}
// NamespaceScoped returns `true` because the storage is namespaced (== org).
func (s *SecureValueRest) NamespaceScoped() bool {
return true
}
// GetSingularName is used by `kubectl` discovery to have singular name representation of resources.
func (s *SecureValueRest) GetSingularName() string {
return s.resource.GetSingularName()
}
// NewList returns an empty `*SecureValueList` that is used by the `List` method.
func (s *SecureValueRest) NewList() runtime.Object {
return s.resource.NewListFunc()
}
// ConvertToTable is used by Kubernetes and converts objects to `metav1.Table`.
func (s *SecureValueRest) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
return s.tableConverter.ConvertToTable(ctx, object, tableOptions)
}
// List calls the inner `store` (persistence) and returns a list of `securevalues` within a `namespace` filtered by the `options`.
func (s *SecureValueRest) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
namespace, ok := request.NamespaceFrom(ctx)
if !ok {
return nil, fmt.Errorf("missing namespace")
}
secureValueList, err := s.storage.List(ctx, xkube.Namespace(namespace), options)
if err != nil {
return nil, fmt.Errorf("failed to list secure values: %w", err)
}
return secureValueList, nil
}
// Get calls the inner `store` (persistence) and returns a `securevalue` by `name`. It will NOT return the decrypted `value`.
func (s *SecureValueRest) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
namespace, ok := request.NamespaceFrom(ctx)
if !ok {
return nil, fmt.Errorf("missing namespace")
}
sv, err := s.storage.Read(ctx, xkube.Namespace(namespace), name)
if err != nil {
if errors.Is(err, contracts.ErrSecureValueNotFound) {
return nil, s.resource.NewNotFound(name)
}
return nil, fmt.Errorf("failed to read secure value: %w", err)
}
return sv, nil
}
// Create a new `securevalue`. Does some validation and allows empty `name` (generated).
func (s *SecureValueRest) Create(
ctx context.Context,
obj runtime.Object,
createValidation rest.ValidateObjectFunc,
options *metav1.CreateOptions,
) (runtime.Object, error) {
sv, ok := obj.(*secretv0alpha1.SecureValue)
if !ok {
return nil, fmt.Errorf("expected SecureValue for create")
}
if err := createValidation(ctx, obj); err != nil {
return nil, err
}
createdSecureValue, err := s.storage.Create(ctx, sv)
if err != nil {
return nil, fmt.Errorf("failed to create secure value: %w", err)
}
return createdSecureValue, nil
}
// Update a `securevalue`'s `value`. The second return parameter indicates whether the resource was newly created.
// Currently does not support "create on update" functionality. If the securevalue does not yet exist, it returns an error.
func (s *SecureValueRest) Update(
ctx context.Context,
name string,
objInfo rest.UpdatedObjectInfo,
createValidation rest.ValidateObjectFunc,
updateValidation rest.ValidateObjectUpdateFunc,
forceAllowCreate bool,
options *metav1.UpdateOptions,
) (runtime.Object, bool, error) {
oldObj, err := s.Get(ctx, name, &metav1.GetOptions{})
if err != nil {
return nil, false, err
}
// Makes sure the UID and ResourceVersion are OK.
// TODO: this also makes it so the labels and annotations are additive, unless we check and remove manually.
newObj, err := objInfo.UpdatedObject(ctx, oldObj)
if err != nil {
return nil, false, fmt.Errorf("k8s updated object: %w", err)
}
if err := updateValidation(ctx, newObj, oldObj); err != nil {
return nil, false, err
}
newSecureValue, ok := newObj.(*secretv0alpha1.SecureValue)
if !ok {
return nil, false, fmt.Errorf("expected SecureValue for update")
}
// TODO: do we need to do this here again? Probably not, but double-check!
newSecureValue.Annotations = xkube.CleanAnnotations(newSecureValue.Annotations)
// Current implementation replaces everything passed in the spec, so it is not a PATCH. Do we want/need to support that?
updatedSecureValue, err := s.storage.Update(ctx, newSecureValue)
if err != nil {
return nil, false, fmt.Errorf("failed to update secure value: %w", err)
}
return updatedSecureValue, false, nil
}
// Delete calls the inner `store` (persistence) in order to delete the `securevalue`.
// The second return parameter `bool` indicates whether the delete was instant or not. It always is for `securevalues`.
func (s *SecureValueRest) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
namespace, ok := request.NamespaceFrom(ctx)
if !ok {
return nil, false, fmt.Errorf("missing namespace")
}
if err := s.storage.Delete(ctx, xkube.Namespace(namespace), name); err != nil {
return nil, false, fmt.Errorf("delete secure value: %w", err)
}
return nil, true, nil
}
// ValidateSecureValue does basic spec validation of a securevalue.
func ValidateSecureValue(sv, oldSv *secretv0alpha1.SecureValue, operation admission.Operation, decryptersAllowList map[string]struct{}) field.ErrorList {
errs := make(field.ErrorList, 0)
// Operation-specific field validation.
switch operation {
case admission.Create:
errs = validateSecureValueCreate(sv)
// If we plan to support PATCH-style updates, we shouldn't be requiring fields to be set.
case admission.Update:
errs = validateSecureValueUpdate(sv, oldSv)
case admission.Delete:
case admission.Connect:
}
// General validations.
if errs := validateDecrypters(sv.Spec.Decrypters, decryptersAllowList); len(errs) > 0 {
return errs
}
return errs
}
// validateSecureValueCreate does basic spec validation of a securevalue for the Create operation.
func validateSecureValueCreate(sv *secretv0alpha1.SecureValue) field.ErrorList {
errs := make(field.ErrorList, 0)
if sv.Spec.Title == "" {
errs = append(errs, field.Required(field.NewPath("spec", "title"), "a `title` is required"))
}
if sv.Spec.Keeper == "" {
errs = append(errs, field.Required(field.NewPath("spec", "keeper"), "a `keeper` is required"))
}
if sv.Spec.Value == "" && sv.Spec.Ref == "" {
errs = append(errs, field.Required(field.NewPath("spec"), "either a `value` or `ref` is required"))
}
if sv.Spec.Value != "" && sv.Spec.Ref != "" {
errs = append(errs, field.Forbidden(field.NewPath("spec"), "only one of `value` or `ref` can be set"))
}
return errs
}
// validateSecureValueUpdate does basic spec validation of a securevalue for the Update operation.
func validateSecureValueUpdate(sv, oldSv *secretv0alpha1.SecureValue) field.ErrorList {
errs := make(field.ErrorList, 0)
// For updates, an `old` object is required.
if oldSv == nil {
errs = append(errs, field.InternalError(field.NewPath("spec"), errors.New("old object is nil")))
return errs
}
// Only validate if one of the fields is being changed/set.
if sv.Spec.Value != "" || sv.Spec.Ref != "" {
if oldSv.Spec.Ref != "" && sv.Spec.Value != "" {
errs = append(errs, field.Forbidden(field.NewPath("spec"), "cannot set `value` when `ref` was already previously set"))
}
if oldSv.Spec.Ref == "" && sv.Spec.Ref != "" {
errs = append(errs, field.Forbidden(field.NewPath("spec"), "cannot set `ref` when `value` was already previously set"))
}
}
// Keeper cannot be changed.
if sv.Spec.Keeper != oldSv.Spec.Keeper {
errs = append(errs, field.Forbidden(field.NewPath("spec"), "the `keeper` cannot be changed"))
}
return errs
}
// validateDecrypters validates that (if populated) the `decrypters` must match "actor_{name}" and must be unique.
func validateDecrypters(decrypters []string, decryptersAllowList map[string]struct{}) field.ErrorList {
errs := make(field.ErrorList, 0)
decrypterNames := make(map[string]struct{}, 0)
for i, decrypter := range decrypters {
// Allow List: decrypters must match exactly and be in the allowed list to be able to decrypt.
// This means an allow list item should have the format "actor_{name}" and not just "{name}".
if len(decryptersAllowList) > 0 {
if _, exists := decryptersAllowList[decrypter]; !exists {
errs = append(
errs,
field.Invalid(field.NewPath("spec", "decrypters", "["+strconv.Itoa(i)+"]"), decrypter, fmt.Sprintf("allowed values: %v", decryptersAllowList)),
)
return errs
}
continue
}
actor, name, found := strings.Cut(strings.TrimSpace(decrypter), "_")
if !found || actor != "actor" || name == "" {
errs = append(
errs,
field.Invalid(field.NewPath("spec", "decrypters", "["+strconv.Itoa(i)+"]"), decrypter, "a decrypter must have the format `actor_{name}`"),
)
continue
}
if _, exists := decrypterNames[name]; exists {
errs = append(
errs,
field.Invalid(field.NewPath("spec", "decrypters", "["+strconv.Itoa(i)+"]"), decrypter, "decrypters must be unique"),
)
continue
}
decrypterNames[name] = struct{}{}
}
return errs
}
@@ -0,0 +1,269 @@
package reststorage
import (
"fmt"
"maps"
"slices"
"testing"
"github.com/stretchr/testify/require"
"k8s.io/apiserver/pkg/admission"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
)
func TestValidateSecureValue(t *testing.T) {
t.Run("when creating a new securevalue", func(t *testing.T) {
validSecureValue := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
Title: "title",
Value: "value",
Keeper: "keeper",
Decrypters: []string{"actor_app1", "actor_app2"},
},
}
t.Run("the `title` must be present", func(t *testing.T) {
sv := validSecureValue.DeepCopy()
sv.Spec.Title = ""
errs := ValidateSecureValue(sv, nil, admission.Create, nil)
require.Len(t, errs, 1)
require.Equal(t, "spec.title", errs[0].Field)
})
t.Run("the `keeper` must be present", func(t *testing.T) {
sv := validSecureValue.DeepCopy()
sv.Spec.Keeper = ""
errs := ValidateSecureValue(sv, nil, admission.Create, nil)
require.Len(t, errs, 1)
require.Equal(t, "spec.keeper", errs[0].Field)
})
t.Run("either a `value` or `ref` must be present but not both", func(t *testing.T) {
sv := validSecureValue.DeepCopy()
sv.Spec.Value = ""
sv.Spec.Ref = ""
errs := ValidateSecureValue(sv, nil, admission.Create, nil)
require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field)
sv.Spec.Value = "value"
sv.Spec.Ref = "value"
errs = ValidateSecureValue(sv, nil, admission.Create, nil)
require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field)
})
})
t.Run("when updating a securevalue", func(t *testing.T) {
t.Run("when trying to switch from a `value` (old) to a `ref` (new), it returns an error", func(t *testing.T) {
oldSv := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
Ref: "", // empty `ref` means a `value` was present.
},
}
sv := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
Ref: "ref",
},
}
errs := ValidateSecureValue(sv, oldSv, admission.Update, nil)
require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field)
})
t.Run("when trying to switch from a `ref` (old) to a `value` (new), it returns an error", func(t *testing.T) {
oldSv := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
Ref: "non-empty",
},
}
sv := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
Value: "value",
},
}
errs := ValidateSecureValue(sv, oldSv, admission.Update, nil)
require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field)
})
t.Run("when both `value` and `ref` are set, it returns an error", func(t *testing.T) {
oldSv := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
Ref: "non-empty",
},
}
sv := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
Value: "value",
Ref: "ref",
},
}
errs := ValidateSecureValue(sv, oldSv, admission.Update, nil)
require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field)
oldSv = &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
Value: "non-empty",
},
}
errs = ValidateSecureValue(sv, oldSv, admission.Update, nil)
require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field)
})
t.Run("when no changes are made, it returns no errors", func(t *testing.T) {
oldSv := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
Title: "old-title",
},
}
sv := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
Title: "new-title",
},
}
errs := ValidateSecureValue(sv, oldSv, admission.Update, nil)
require.Empty(t, errs)
})
t.Run("when the old object is `nil` it returns an error", func(t *testing.T) {
sv := &secretv0alpha1.SecureValue{}
errs := ValidateSecureValue(sv, nil, admission.Update, nil)
require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field)
})
t.Run("when trying to change the `keeper`, it returns an error", func(t *testing.T) {
oldSv := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
Keeper: "a-keeper",
},
}
sv := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
Keeper: "another-keeper",
},
}
errs := ValidateSecureValue(sv, oldSv, admission.Update, nil)
require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field)
})
})
t.Run("`decrypters` must have unique items", func(t *testing.T) {
sv := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
Title: "title", Keeper: "keeper", Ref: "ref",
Decrypters: []string{
"actor_app1",
"actor_app1",
},
},
}
errs := ValidateSecureValue(sv, nil, admission.Create, nil)
require.Len(t, errs, 1)
require.Equal(t, "spec.decrypters.[1]", errs[0].Field)
})
t.Run("`decrypters` must match the expected format", func(t *testing.T) {
sv := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
Title: "title", Keeper: "keeper", Ref: "ref",
Decrypters: []string{
"app1",
"_app1",
"actr_app1",
"actor_ ",
"actor_",
},
},
}
errs := ValidateSecureValue(sv, nil, admission.Create, nil)
require.Len(t, errs, len(sv.Spec.Decrypters))
for i, err := range errs {
require.Equal(t, fmt.Sprintf("spec.decrypters.[%d]", i), err.Field)
require.Contains(t, err.Error(), "a decrypter must have the format `actor_{name}`")
}
})
t.Run("when set, the `decrypters` must be one of the allowed in the allow list", func(t *testing.T) {
allowList := map[string]struct{}{"actor_app1": {}, "actor_app2": {}}
decrypters := slices.Collect(maps.Keys(allowList))
t.Run("no matches, returns an error", func(t *testing.T) {
sv := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
Title: "title", Keeper: "keeper", Ref: "ref",
Decrypters: []string{"actor_app3"},
},
}
errs := ValidateSecureValue(sv, nil, admission.Create, allowList)
require.Len(t, errs, 1)
})
t.Run("no decrypters, returns no error", func(t *testing.T) {
sv := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
Title: "title", Keeper: "keeper", Ref: "ref",
Decrypters: []string{},
},
}
errs := ValidateSecureValue(sv, nil, admission.Create, allowList)
require.Empty(t, errs)
})
t.Run("one match, returns no errors", func(t *testing.T) {
sv := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
Title: "title", Keeper: "keeper", Ref: "ref",
Decrypters: []string{decrypters[0]},
},
}
errs := ValidateSecureValue(sv, nil, admission.Create, allowList)
require.Empty(t, errs)
})
t.Run("all matches, returns no errors", func(t *testing.T) {
sv := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
Title: "title", Keeper: "keeper", Ref: "ref",
Decrypters: decrypters,
},
}
errs := ValidateSecureValue(sv, nil, admission.Create, allowList)
require.Empty(t, errs)
})
})
}
@@ -0,0 +1,24 @@
package xkube
import "github.com/grafana/grafana/pkg/apimachinery/utils"
var (
// Exclude these annotations
skipAnnotations = map[string]bool{
"kubectl.kubernetes.io/last-applied-configuration": true, // force server side apply
utils.AnnoKeyCreatedBy: true,
utils.AnnoKeyUpdatedBy: true,
utils.AnnoKeyUpdatedTimestamp: true,
}
)
func CleanAnnotations(anno map[string]string) map[string]string {
copy := make(map[string]string)
for k, v := range anno {
if skipAnnotations[k] {
continue
}
copy[k] = v
}
return copy
}
+9
View File
@@ -0,0 +1,9 @@
package xkube
import "k8s.io/apimachinery/pkg/util/validation/field"
// ErrorLister is an interface compatible with errors that also returns a list of Kubernetes field errors.
type ErrorLister interface {
error
ErrorList() field.ErrorList
}
+19 -21
View File
@@ -4,25 +4,6 @@ package semconv
import "go.opentelemetry.io/otel/attribute"
// Describes Grafana service attributes.
const (
// GrafanaServiceNameKey is the attribute Key conforming to the
// "grafana.service.name" semantic conventions. It represents the service
// name.
//
// Type: string
// RequirementLevel: Optional
// Stability: stable
// Examples: 'grafana-apiserver'
grafanaServiceNameKey = attribute.Key("grafana.service.name")
)
// GrafanaServiceName returns an attribute KeyValue conforming to the
// "grafana.service.name" semantic conventions. It represents the service name.
func GrafanaServiceName(val string) attribute.KeyValue {
return grafanaServiceNameKey.String(val)
}
// Describes Grafana datasource attributes.
const (
// GrafanaDatasourceTypeKey is the attribute Key conforming to the
@@ -131,8 +112,6 @@ var (
GrafanaPluginTypeApp = grafanaPluginTypeKey.String("app")
// Renderer Plugin
GrafanaPluginTypeRenderer = grafanaPluginTypeKey.String("renderer")
// Secret Manager Plugin
GrafanaPluginTypeSecretmanager = grafanaPluginTypeKey.String("secretmanager")
)
// GrafanaPluginId returns an attribute KeyValue conforming to the
@@ -140,3 +119,22 @@ var (
func GrafanaPluginId(val string) attribute.KeyValue {
return grafanaPluginIdKey.String(val)
}
// Describes Grafana service attributes.
const (
// GrafanaServiceNameKey is the attribute Key conforming to the
// "grafana.service.name" semantic conventions. It represents the service
// name.
//
// Type: string
// RequirementLevel: Optional
// Stability: stable
// Examples: 'grafana-apiserver'
grafanaServiceNameKey = attribute.Key("grafana.service.name")
)
// GrafanaServiceName returns an attribute KeyValue conforming to the
// "grafana.service.name" semantic conventions. It represents the service name.
func GrafanaServiceName(val string) attribute.KeyValue {
return grafanaServiceNameKey.String(val)
}
+1 -5
View File
@@ -31,10 +31,6 @@ groups:
value: "renderer"
brief: 'Renderer Plugin'
stability: stable
- id: secretmanager
value: "secretmanager"
brief: 'Secret Manager Plugin'
stability: stable
brief: The plugin type.
examples: datasource
stability: stable
stability: stable
-2
View File
@@ -363,8 +363,6 @@ var wireBasicSet = wire.NewSet(
loginattemptimpl.ProvideService,
wire.Bind(new(loginattempt.Service), new(*loginattemptimpl.Service)),
secretsMigrations.ProvideDataSourceMigrationService,
secretsMigrations.ProvideMigrateToPluginService,
secretsMigrations.ProvideMigrateFromPluginService,
secretsMigrations.ProvideSecretMigrationProvider,
wire.Bind(new(secretsMigrations.SecretMigrationProvider), new(*secretsMigrations.SecretMigrationProviderImpl)),
resourcepermissions.NewActionSetService,
+20 -18
View File
@@ -76,24 +76,26 @@ func ProvidePermissionRegistry() PermissionRegistry {
func newPermissionRegistry() *permissionRegistry {
// defaultKindScopes maps the most specific accepted scope prefix for a given kind (folders, dashboards, etc)
defaultKindScopes := map[string]string{
"teams": "teams:id:",
"users": "users:id:",
"datasources": "datasources:uid:",
"dashboards": "dashboards:uid:",
"folders": "folders:uid:",
"annotations": "annotations:type:",
"apikeys": "apikeys:id:",
"orgs": "orgs:id:",
"plugins": "plugins:id:",
"provisioners": "provisioners:",
"reports": "reports:id:",
"permissions": "permissions:type:",
"serviceaccounts": "serviceaccounts:id:",
"settings": "settings:",
"global.users": "global.users:id:",
"roles": "roles:uid:",
"services": "services:",
"receivers": "receivers:uid:",
"teams": "teams:id:",
"users": "users:id:",
"datasources": "datasources:uid:",
"dashboards": "dashboards:uid:",
"folders": "folders:uid:",
"annotations": "annotations:type:",
"apikeys": "apikeys:id:",
"orgs": "orgs:id:",
"plugins": "plugins:id:",
"provisioners": "provisioners:",
"reports": "reports:id:",
"permissions": "permissions:type:",
"serviceaccounts": "serviceaccounts:id:",
"settings": "settings:",
"global.users": "global.users:id:",
"roles": "roles:uid:",
"services": "services:",
"receivers": "receivers:uid:",
"secret.securevalues": "secret.securevalues:uid:",
"secret.keepers": "secret.keepers:uid:",
}
return &permissionRegistry{
actionScopePrefixes: make(map[string]PrefixSet, 200),
+4
View File
@@ -63,6 +63,10 @@ func newMapper() mapper {
"iam.grafana.app": {
"teams": newResourceTranslation("teams", "id", false),
},
"secret.grafana.app": {
"securevalues": newResourceTranslation("secret.securevalues", "uid", false),
"keepers": newResourceTranslation("secret.keepers", "uid", false),
},
}
}
@@ -1,21 +1,21 @@
SELECT p.kind, p.attribute, p.identifier, p.scope FROM {{ .Ident .PermissionTable }} as p
INNER JOIN (
SELECT role_id FROM {{ .Ident .BuiltinRoleTable }} as br WHERE (br.role = {{ .Arg .Query.Role }} AND (br.org_id = {{ .Arg .Query.OrgID }} OR br.org_id = 0))
{{ if .Query.IsServerAdmin }}
OR (br.role = 'Grafana Admin')
{{ end }}
{{ if .Query.UserID }}
UNION ALL
SELECT role_id FROM {{ .Ident .UserRoleTable }} as ur WHERE ur.user_id = {{ .Arg .Query.UserID }} AND (ur.org_id = {{ .Arg .Query.OrgID }} OR ur.org_id = 0)
{{ end }}
{{ if .Query.TeamIDs }}
UNION ALL
SELECT role_id FROM {{ .Ident .TeamRoleTable }} as tr WHERE tr.team_id IN ({{ .ArgList .Query.TeamIDs }}) AND tr.org_id = {{ .Arg .Query.OrgID }}
{{ end }}
) as roles ON p.role_id = roles.role_id
WHERE
{{ if .Query.ActionSets }}
p.action IN ({{ .ArgList .Query.ActionSets }}, {{ .Arg .Query.Action }})
{{ else }}
p.action = {{ .Arg .Query.Action }}
{{ end }}
AND p.role_id IN (
SELECT role_id FROM {{ .Ident .BuiltinRoleTable }} as br WHERE (br.role = {{ .Arg .Query.Role }} AND (br.org_id = {{ .Arg .Query.OrgID }} OR br.org_id = 0))
{{ if .Query.IsServerAdmin }}
OR (br.role = 'Grafana Admin')
{{ end }}
{{ if .Query.UserID }}
UNION
SELECT role_id FROM {{ .Ident .UserRoleTable }} as ur WHERE ur.user_id = {{ .Arg .Query.UserID }} AND (ur.org_id = {{ .Arg .Query.OrgID }} OR ur.org_id = 0)
{{ end }}
{{ if .Query.TeamIDs }}
UNION
SELECT role_id FROM {{ .Ident .TeamRoleTable }} as tr WHERE tr.team_id IN ({{ .ArgList .Query.TeamIDs }}) AND tr.org_id = {{ .Arg .Query.OrgID }}
{{ end }}
)
{{ end }}
@@ -1,8 +1,8 @@
SELECT p.kind, p.attribute, p.identifier, p.scope FROM `grafana`.`permission` as p
INNER JOIN (
SELECT role_id FROM `grafana`.`builtin_role` as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
UNION ALL
SELECT role_id FROM `grafana`.`user_role` as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
) as roles ON p.role_id = roles.role_id
WHERE
p.action IN ('folders:edit', 'folders:admin', 'folders:create')
AND p.role_id IN (
SELECT role_id FROM `grafana`.`builtin_role` as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
UNION
SELECT role_id FROM `grafana`.`user_role` as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
)
@@ -1,9 +1,9 @@
SELECT p.kind, p.attribute, p.identifier, p.scope FROM `grafana`.`permission` as p
INNER JOIN (
SELECT role_id FROM `grafana`.`builtin_role` as br WHERE (br.role = 'Admin' AND (br.org_id = 1 OR br.org_id = 0))
OR (br.role = 'Grafana Admin')
UNION ALL
SELECT role_id FROM `grafana`.`user_role` as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
) as roles ON p.role_id = roles.role_id
WHERE
p.action = 'folders:read'
AND p.role_id IN (
SELECT role_id FROM `grafana`.`builtin_role` as br WHERE (br.role = 'Admin' AND (br.org_id = 1 OR br.org_id = 0))
OR (br.role = 'Grafana Admin')
UNION
SELECT role_id FROM `grafana`.`user_role` as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
)
@@ -1,6 +1,6 @@
SELECT p.kind, p.attribute, p.identifier, p.scope FROM `grafana`.`permission` as p
INNER JOIN (
SELECT role_id FROM `grafana`.`builtin_role` as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
) as roles ON p.role_id = roles.role_id
WHERE
p.action = 'folders:read'
AND p.role_id IN (
SELECT role_id FROM `grafana`.`builtin_role` as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
)
@@ -1,10 +1,10 @@
SELECT p.kind, p.attribute, p.identifier, p.scope FROM `grafana`.`permission` as p
INNER JOIN (
SELECT role_id FROM `grafana`.`builtin_role` as br WHERE (br.role = 'None' AND (br.org_id = 1 OR br.org_id = 0))
UNION ALL
SELECT role_id FROM `grafana`.`user_role` as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
UNION ALL
SELECT role_id FROM `grafana`.`team_role` as tr WHERE tr.team_id IN (1, 2) AND tr.org_id = 1
) as roles ON p.role_id = roles.role_id
WHERE
p.action = 'folders:read'
AND p.role_id IN (
SELECT role_id FROM `grafana`.`builtin_role` as br WHERE (br.role = 'None' AND (br.org_id = 1 OR br.org_id = 0))
UNION
SELECT role_id FROM `grafana`.`user_role` as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
UNION
SELECT role_id FROM `grafana`.`team_role` as tr WHERE tr.team_id IN (1, 2) AND tr.org_id = 1
)
@@ -1,8 +1,8 @@
SELECT p.kind, p.attribute, p.identifier, p.scope FROM `grafana`.`permission` as p
INNER JOIN (
SELECT role_id FROM `grafana`.`builtin_role` as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
UNION ALL
SELECT role_id FROM `grafana`.`user_role` as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
) as roles ON p.role_id = roles.role_id
WHERE
p.action = 'folders:read'
AND p.role_id IN (
SELECT role_id FROM `grafana`.`builtin_role` as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
UNION
SELECT role_id FROM `grafana`.`user_role` as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
)
@@ -1,8 +1,8 @@
SELECT p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
INNER JOIN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
UNION ALL
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
) as roles ON p.role_id = roles.role_id
WHERE
p.action IN ('folders:edit', 'folders:admin', 'folders:create')
AND p.role_id IN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
UNION
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
)
@@ -1,9 +1,9 @@
SELECT p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
INNER JOIN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Admin' AND (br.org_id = 1 OR br.org_id = 0))
OR (br.role = 'Grafana Admin')
UNION ALL
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
) as roles ON p.role_id = roles.role_id
WHERE
p.action = 'folders:read'
AND p.role_id IN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Admin' AND (br.org_id = 1 OR br.org_id = 0))
OR (br.role = 'Grafana Admin')
UNION
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
)
@@ -1,6 +1,6 @@
SELECT p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
INNER JOIN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
) as roles ON p.role_id = roles.role_id
WHERE
p.action = 'folders:read'
AND p.role_id IN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
)
@@ -1,10 +1,10 @@
SELECT p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
INNER JOIN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'None' AND (br.org_id = 1 OR br.org_id = 0))
UNION ALL
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
UNION ALL
SELECT role_id FROM "grafana"."team_role" as tr WHERE tr.team_id IN (1, 2) AND tr.org_id = 1
) as roles ON p.role_id = roles.role_id
WHERE
p.action = 'folders:read'
AND p.role_id IN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'None' AND (br.org_id = 1 OR br.org_id = 0))
UNION
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
UNION
SELECT role_id FROM "grafana"."team_role" as tr WHERE tr.team_id IN (1, 2) AND tr.org_id = 1
)
@@ -1,8 +1,8 @@
SELECT p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
INNER JOIN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
UNION ALL
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
) as roles ON p.role_id = roles.role_id
WHERE
p.action = 'folders:read'
AND p.role_id IN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
UNION
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
)
@@ -1,8 +1,8 @@
SELECT p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
INNER JOIN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
UNION ALL
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
) as roles ON p.role_id = roles.role_id
WHERE
p.action IN ('folders:edit', 'folders:admin', 'folders:create')
AND p.role_id IN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
UNION
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
)
@@ -1,9 +1,9 @@
SELECT p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
INNER JOIN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Admin' AND (br.org_id = 1 OR br.org_id = 0))
OR (br.role = 'Grafana Admin')
UNION ALL
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
) as roles ON p.role_id = roles.role_id
WHERE
p.action = 'folders:read'
AND p.role_id IN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Admin' AND (br.org_id = 1 OR br.org_id = 0))
OR (br.role = 'Grafana Admin')
UNION
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
)
@@ -1,6 +1,6 @@
SELECT p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
INNER JOIN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
) as roles ON p.role_id = roles.role_id
WHERE
p.action = 'folders:read'
AND p.role_id IN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
)
@@ -1,10 +1,10 @@
SELECT p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
INNER JOIN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'None' AND (br.org_id = 1 OR br.org_id = 0))
UNION ALL
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
UNION ALL
SELECT role_id FROM "grafana"."team_role" as tr WHERE tr.team_id IN (1, 2) AND tr.org_id = 1
) as roles ON p.role_id = roles.role_id
WHERE
p.action = 'folders:read'
AND p.role_id IN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'None' AND (br.org_id = 1 OR br.org_id = 0))
UNION
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
UNION
SELECT role_id FROM "grafana"."team_role" as tr WHERE tr.team_id IN (1, 2) AND tr.org_id = 1
)
@@ -1,8 +1,8 @@
SELECT p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
INNER JOIN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
UNION ALL
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
) as roles ON p.role_id = roles.role_id
WHERE
p.action = 'folders:read'
AND p.role_id IN (
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
UNION
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
)
@@ -122,11 +122,7 @@ func (s *FakeDataSourceService) GetHTTPTransport(ctx context.Context, ds *dataso
}
func (s *FakeDataSourceService) DecryptedValues(ctx context.Context, ds *datasources.DataSource) (map[string]string, error) {
if s.SimulatePluginFailure {
return nil, datasources.ErrDatasourceSecretsPluginUserFriendly{Err: "unknown error"}
}
values := make(map[string]string)
return values, nil
return make(map[string]string), nil
}
func (s *FakeDataSourceService) DecryptedValue(ctx context.Context, ds *datasources.DataSource, key string) (string, bool, error) {
-9
View File
@@ -145,15 +145,6 @@ func (ds DataSource) AllowedCookies() []string {
return []string{}
}
// Specific error type for grpc secrets management so that we can show more detailed plugin errors to users
type ErrDatasourceSecretsPluginUserFriendly struct {
Err string
}
func (e ErrDatasourceSecretsPluginUserFriendly) Error() string {
return e.Err
}
// ----------------------
// COMMANDS
+3 -2
View File
@@ -376,7 +376,7 @@ var (
{
Name: "frontendSandboxMonitorOnly",
Description: "Enables monitor only in the plugin frontend sandbox (if enabled)",
Stage: FeatureStageExperimental,
Stage: FeatureStagePrivatePreview,
FrontendOnly: true,
Owner: grafanaPluginsPlatformSquad,
},
@@ -1421,8 +1421,9 @@ var (
{
Name: "pluginsSriChecks",
Description: "Enables SRI checks for plugin assets",
Stage: FeatureStageExperimental,
Stage: FeatureStageGeneralAvailability,
Owner: grafanaPluginsPlatformSquad,
Expression: "false", // disabled by default
},
{
Name: "unifiedStorageBigObjectsSupport",
+2 -2
View File
@@ -49,7 +49,7 @@ enableDatagridEditing,preview,@grafana/dataviz-squad,false,false,true
extraThemes,experimental,@grafana/grafana-frontend-platform,false,false,true
lokiPredefinedOperations,experimental,@grafana/observability-logs,false,false,true
pluginsFrontendSandbox,privatePreview,@grafana/plugins-platform-backend,false,false,false
frontendSandboxMonitorOnly,experimental,@grafana/plugins-platform-backend,false,false,true
frontendSandboxMonitorOnly,privatePreview,@grafana/plugins-platform-backend,false,false,true
pluginsDetailsRightPanel,privatePreview,@grafana/plugins-platform-backend,false,false,true
sqlDatasourceDatabaseSelection,preview,@grafana/dataviz-squad,false,false,true
recordedQueriesMulti,GA,@grafana/observability-metrics,false,false,false
@@ -186,7 +186,7 @@ unifiedStorageSearch,experimental,@grafana/search-and-storage,false,false,false
unifiedStorageSearchSprinkles,experimental,@grafana/search-and-storage,false,false,false
unifiedStorageSearchPermissionFiltering,experimental,@grafana/search-and-storage,false,false,false
managedDualWriter,experimental,@grafana/search-and-storage,false,false,false
pluginsSriChecks,experimental,@grafana/plugins-platform-backend,false,false,false
pluginsSriChecks,GA,@grafana/plugins-platform-backend,false,false,false
unifiedStorageBigObjectsSupport,experimental,@grafana/search-and-storage,false,false,false
timeRangeProvider,experimental,@grafana/grafana-frontend-platform,false,false,false
prometheusUsesCombobox,experimental,@grafana/oss-big-tent,false,false,false
1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
49 extraThemes experimental @grafana/grafana-frontend-platform false false true
50 lokiPredefinedOperations experimental @grafana/observability-logs false false true
51 pluginsFrontendSandbox privatePreview @grafana/plugins-platform-backend false false false
52 frontendSandboxMonitorOnly experimental privatePreview @grafana/plugins-platform-backend false false true
53 pluginsDetailsRightPanel privatePreview @grafana/plugins-platform-backend false false true
54 sqlDatasourceDatabaseSelection preview @grafana/dataviz-squad false false true
55 recordedQueriesMulti GA @grafana/observability-metrics false false false
186 unifiedStorageSearchSprinkles experimental @grafana/search-and-storage false false false
187 unifiedStorageSearchPermissionFiltering experimental @grafana/search-and-storage false false false
188 managedDualWriter experimental @grafana/search-and-storage false false false
189 pluginsSriChecks experimental GA @grafana/plugins-platform-backend false false false
190 unifiedStorageBigObjectsSupport experimental @grafana/search-and-storage false false false
191 timeRangeProvider experimental @grafana/grafana-frontend-platform false false false
192 prometheusUsesCombobox experimental @grafana/oss-big-tent false false false
+14 -7
View File
@@ -1913,12 +1913,15 @@
{
"metadata": {
"name": "frontendSandboxMonitorOnly",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-07-05T11:48:25Z"
"resourceVersion": "1741965568775",
"creationTimestamp": "2023-07-05T11:48:25Z",
"annotations": {
"grafana.app/updatedTimestamp": "2025-03-14 15:19:28.77575 +0000 UTC"
}
},
"spec": {
"description": "Enables monitor only in the plugin frontend sandbox (if enabled)",
"stage": "experimental",
"stage": "privatePreview",
"codeowner": "@grafana/plugins-platform-backend",
"frontend": true
}
@@ -3281,13 +3284,17 @@
{
"metadata": {
"name": "pluginsSriChecks",
"resourceVersion": "1727785264632",
"creationTimestamp": "2024-10-04T12:55:09Z"
"resourceVersion": "1742405194835",
"creationTimestamp": "2024-10-04T12:55:09Z",
"annotations": {
"grafana.app/updatedTimestamp": "2025-03-19 17:26:34.83562 +0000 UTC"
}
},
"spec": {
"description": "Enables SRI checks for plugin assets",
"stage": "experimental",
"codeowner": "@grafana/plugins-platform-backend"
"stage": "GA",
"codeowner": "@grafana/plugins-platform-backend",
"expression": "false"
}
},
{
+8 -2
View File
@@ -23,6 +23,8 @@ import (
"github.com/grafana/grafana/pkg/services/star"
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlesimpl"
"github.com/grafana/grafana/pkg/setting"
"github.com/open-feature/go-sdk/openfeature"
)
type ServiceImpl struct {
@@ -34,6 +36,7 @@ type ServiceImpl struct {
pluginSettings pluginsettings.Service
starService star.Service
features featuremgmt.FeatureToggles
openFeature *featuremgmt.OpenFeatureService
dashboardService dashboards.DashboardService
accesscontrolService ac.Service
kvStore kvstore.KVStore
@@ -54,7 +57,7 @@ type NavigationAppConfig struct {
func ProvideService(cfg *setting.Cfg, accessControl ac.AccessControl, pluginStore pluginstore.Store, pluginSettings pluginsettings.Service, starService star.Service,
features featuremgmt.FeatureToggles, dashboardService dashboards.DashboardService, accesscontrolService ac.Service, kvStore kvstore.KVStore, apiKeyService apikey.Service,
license licensing.Licensing, authnService authn.Service) navtree.Service {
license licensing.Licensing, authnService authn.Service, openFeature *featuremgmt.OpenFeatureService) navtree.Service {
service := &ServiceImpl{
cfg: cfg,
log: log.New("navtree service"),
@@ -64,6 +67,7 @@ func ProvideService(cfg *setting.Cfg, accessControl ac.AccessControl, pluginStor
pluginSettings: pluginSettings,
starService: starService,
features: features,
openFeature: openFeature,
dashboardService: dashboardService,
accesscontrolService: accesscontrolService,
kvStore: kvStore,
@@ -80,6 +84,7 @@ func ProvideService(cfg *setting.Cfg, accessControl ac.AccessControl, pluginStor
func (s *ServiceImpl) GetNavTree(c *contextmodel.ReqContext, prefs *pref.Preference) (*navtree.NavTreeRoot, error) {
hasAccess := ac.HasAccess(s.accessControl, c)
treeRoot := &navtree.NavTreeRoot{}
ctx := c.Req.Context()
treeRoot.AddSection(s.getHomeNode(c, prefs))
@@ -184,7 +189,8 @@ func (s *ServiceImpl) GetNavTree(c *contextmodel.ReqContext, prefs *pref.Prefere
treeRoot.RemoveSectionByID(navtree.NavIDCfg)
}
if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagPinNavItems) && c.IsSignedIn {
enabled := s.openFeature.Client.Boolean(ctx, featuremgmt.FlagPinNavItems, true, openfeature.TransactionContext(ctx))
if enabled && c.IsSignedIn {
treeRoot.AddSection(&navtree.NavLink{
Text: "Bookmarks",
Id: navtree.NavIDBookmarks,
@@ -7,7 +7,6 @@ import (
"time"
"github.com/go-openapi/strfmt"
"github.com/mohae/deepcopy"
amv2 "github.com/prometheus/alertmanager/api/v2/models"
"github.com/prometheus/alertmanager/config"
"github.com/prometheus/common/model"
@@ -698,26 +697,6 @@ func (c *PostableUserConfig) validate() error {
return nil
}
// Decrypt returns a copy of the configuration struct with decrypted secure settings in receivers.
func (c *PostableUserConfig) Decrypt(decryptFn func(payload []byte) ([]byte, error)) (PostableUserConfig, error) {
newCfg, ok := deepcopy.Copy(c).(*PostableUserConfig)
if !ok {
return PostableUserConfig{}, fmt.Errorf("failed to copy config")
}
// Iterate through receivers and decrypt secure settings.
for _, rcv := range newCfg.AlertmanagerConfig.Receivers {
for _, gmr := range rcv.PostableGrafanaReceivers.GrafanaManagedReceivers {
decrypted, err := gmr.DecryptSecureSettings(decryptFn)
if err != nil {
return PostableUserConfig{}, err
}
gmr.SecureSettings = decrypted
}
}
return *newCfg, nil
}
// GetGrafanaReceiverMap returns a map that associates UUIDs to grafana receivers
func (c *PostableUserConfig) GetGrafanaReceiverMap() map[string]*PostableGrafanaReceiver {
UIDs := make(map[string]*PostableGrafanaReceiver)
+46 -26
View File
@@ -255,17 +255,22 @@ func (am *Alertmanager) CompareAndSendConfiguration(ctx context.Context, config
if err := am.autogenFn(ctx, am.log, am.orgID, &c.AlertmanagerConfig, true); err != nil {
return err
}
decrypted, err := am.decryptConfiguration(ctx, c)
rawDecrypted, configHash, err := am.decryptConfiguration(ctx, c)
if err != nil {
return err
}
// Send the configuration only if we need to.
if !am.shouldSendConfig(ctx, decrypted) {
if !am.shouldSendConfig(ctx, configHash) {
return nil
}
isDefault, err := am.isDefaultConfiguration(decrypted)
isDefault, err := am.isDefaultConfiguration(configHash)
if err != nil {
return err
}
decrypted, err := notifier.Load(rawDecrypted)
if err != nil {
return err
}
@@ -273,27 +278,37 @@ func (am *Alertmanager) CompareAndSendConfiguration(ctx context.Context, config
return am.sendConfiguration(ctx, decrypted, config.ConfigurationHash, config.CreatedAt, isDefault)
}
func (am *Alertmanager) isDefaultConfiguration(cfg *apimodels.PostableUserConfig) (bool, error) {
rawCfg, err := json.Marshal(cfg)
if err != nil {
return false, err
}
configHash := fmt.Sprintf("%x", md5.Sum(rawCfg))
return configHash == am.defaultConfigHash, nil
func (am *Alertmanager) isDefaultConfiguration(configHash [16]byte) (bool, error) {
return fmt.Sprintf("%x", configHash) == am.defaultConfigHash, nil
}
func (am *Alertmanager) decryptConfiguration(ctx context.Context, cfg *apimodels.PostableUserConfig) (*apimodels.PostableUserConfig, error) {
// decryptConfiguration decrypts the configuration in-place and returns the decrypted configuration alongside its hash.
// Should not be used outside of this package and the specific use case of decrypting the configuration before sending
// it to the remote Alertmanager.
func (am *Alertmanager) decryptConfiguration(ctx context.Context, cfg *apimodels.PostableUserConfig) ([]byte, [16]byte, error) {
fn := func(payload []byte) ([]byte, error) {
return am.decrypt(ctx, payload)
}
decrypted, err := cfg.Decrypt(fn)
if err != nil {
return nil, fmt.Errorf("unable to decrypt the configuration: %w", err)
// Iterate through receivers and decrypt secure settings.
// It's not necessary to be careful about not modifying the original, as it's used only in a specific context where
// the config is read from json and then immediately sent to the remote Alertmanager.
for _, rcv := range cfg.AlertmanagerConfig.Receivers {
for _, gmr := range rcv.PostableGrafanaReceivers.GrafanaManagedReceivers {
decrypted, err := gmr.DecryptSecureSettings(fn)
if err != nil {
return nil, [16]byte{}, fmt.Errorf("unable to decrypt settings on receiver %q (uid: %q): %w", gmr.Name, gmr.UID, err)
}
gmr.SecureSettings = decrypted
}
}
return &decrypted, nil
rawDecrypted, err := json.Marshal(cfg)
if err != nil {
return nil, [16]byte{}, fmt.Errorf("unable to marshal decrypted configuration: %w", err)
}
return rawDecrypted, md5.Sum(rawDecrypted), nil
}
func (am *Alertmanager) sendConfiguration(ctx context.Context, decrypted *apimodels.PostableUserConfig, hash string, createdAt int64, isDefault bool) error {
@@ -345,7 +360,12 @@ func (am *Alertmanager) SaveAndApplyConfig(ctx context.Context, cfg *apimodels.P
if err := am.autogenFn(ctx, am.log, am.orgID, &cfg.AlertmanagerConfig, false); err != nil {
return err
}
decrypted, err := am.decryptConfiguration(ctx, cfg)
rawDecrypted, _, err := am.decryptConfiguration(ctx, cfg)
if err != nil {
return err
}
decrypted, err := notifier.Load(rawDecrypted)
if err != nil {
return err
}
@@ -364,7 +384,12 @@ func (am *Alertmanager) SaveAndApplyDefaultConfig(ctx context.Context) error {
if err := am.autogenFn(ctx, am.log, am.orgID, &c.AlertmanagerConfig, true); err != nil {
return err
}
decrypted, err := am.decryptConfiguration(ctx, c)
rawDecrypted, _, err := am.decryptConfiguration(ctx, c)
if err != nil {
return err
}
decrypted, err := notifier.Load(rawDecrypted)
if err != nil {
return err
}
@@ -634,7 +659,7 @@ func (am *Alertmanager) getFullState(ctx context.Context) (string, error) {
// shouldSendConfig compares the remote Alertmanager configuration with our local one.
// It returns true if the configurations are different.
func (am *Alertmanager) shouldSendConfig(ctx context.Context, config *apimodels.PostableUserConfig) bool {
func (am *Alertmanager) shouldSendConfig(ctx context.Context, hash [16]byte) bool {
rc, err := am.mimirClient.GetGrafanaAlertmanagerConfig(ctx)
if err != nil {
// Log the error and return true so we try to upload our config anyway.
@@ -651,12 +676,7 @@ func (am *Alertmanager) shouldSendConfig(ctx context.Context, config *apimodels.
am.log.Error("Unable to marshal the remote Alertmanager configuration for comparison", "err", err)
return true
}
rawInternal, err := json.Marshal(config)
if err != nil {
am.log.Error("Unable to marshal the internal Alertmanager configuration for comparison", "err", err)
return true
}
return md5.Sum(rawRemote) != md5.Sum(rawInternal)
return md5.Sum(rawRemote) != hash
}
// shouldSendState compares the remote Alertmanager state with our local one.
@@ -23,6 +23,8 @@ import (
"github.com/grafana/alerting/definition"
alertingModels "github.com/grafana/alerting/models"
"github.com/grafana/alerting/notify"
"gopkg.in/yaml.v3"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
@@ -39,13 +41,12 @@ import (
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tests/testsuite"
"github.com/grafana/grafana/pkg/util"
"gopkg.in/yaml.v3"
)
const (
// Valid Grafana Alertmanager configurations.
testGrafanaConfig = `{"template_files":{},"alertmanager_config":{"route":{"receiver":"grafana-default-email","group_by":["grafana_folder","alertname"]},"receivers":[{"name":"grafana-default-email","grafana_managed_receiver_configs":[{"uid":"","name":"some other name","type":"email","disableResolveMessage":false,"settings":{"addresses":"\u003cexample@email.com\u003e"}}]}]}}`
testGrafanaConfigWithSecret = `{"template_files":{},"alertmanager_config":{"route":{"receiver":"grafana-default-email","group_by":["grafana_folder","alertname"]},"receivers":[{"name":"grafana-default-email","grafana_managed_receiver_configs":[{"uid":"dde6ntuob69dtf","name":"WH","type":"webhook","disableResolveMessage":false,"settings":{"url":"http://localhost:8080","username":"test"},"secureSettings":{"password":"test"}}]}]}}`
testGrafanaConfig = `{"template_files":{},"alertmanager_config":{"time_intervals":[{"name":"weekends","time_intervals":[{"weekdays":["saturday","sunday"],"location":"Africa/Accra"}]}],"route":{"receiver":"grafana-default-email","group_by":["grafana_folder","alertname"]},"receivers":[{"name":"grafana-default-email","grafana_managed_receiver_configs":[{"uid":"","name":"some other name","type":"email","disableResolveMessage":false,"settings":{"addresses":"\u003cexample@email.com\u003e"}}]}]}}`
testGrafanaConfigWithSecret = `{"template_files":{},"alertmanager_config":{"time_intervals":[{"name":"weekends","time_intervals":[{"weekdays":["saturday","sunday"],"location":"Africa/Accra"}]}],"route":{"receiver":"grafana-default-email","group_by":["grafana_folder","alertname"]},"receivers":[{"name":"grafana-default-email","grafana_managed_receiver_configs":[{"uid":"dde6ntuob69dtf","name":"WH","type":"webhook","disableResolveMessage":false,"settings":{"url":"http://localhost:8080","username":"test"},"secureSettings":{"password":"test"}}]}]}}`
testGrafanaDefaultConfigWithDifferentFieldOrder = `{"alertmanager_config":{"route":{"group_by":["alertname","grafana_folder"],"receiver":"grafana-default-email"},"receivers":[{"grafana_managed_receiver_configs":[{"uid":"","name":"email receiver","type":"email","settings":{"addresses":"<example@email.com>"}}],"name":"grafana-default-email"}]}}`
// Valid Alertmanager state base64 encoded.
@@ -287,14 +288,14 @@ func TestCompareAndSendConfiguration(t *testing.T) {
strings.Replace(testGrafanaConfigWithSecret, `"password":"test"`, `"password":"!"`, 1),
NoopAutogenFn,
nil,
"unable to decrypt the configuration: failed to decode value for key 'password': illegal base64 data at input byte 0",
`unable to decrypt settings on receiver "WH" (uid: "dde6ntuob69dtf"): failed to decode value for key 'password': illegal base64 data at input byte 0`,
},
{
"decrypt error",
testGrafanaConfigWithSecret,
NoopAutogenFn,
nil,
fmt.Sprintf("unable to decrypt the configuration: failed to decrypt value for key 'password': %s", errTest.Error()),
fmt.Sprintf(`unable to decrypt settings on receiver "WH" (uid: "dde6ntuob69dtf"): failed to decrypt value for key 'password': %s`, errTest.Error()),
},
{
"error from autogen function",
@@ -443,7 +444,9 @@ func Test_isDefaultConfiguration(t *testing.T) {
defaultConfig: string(rawDefaultCfg),
defaultConfigHash: fmt.Sprintf("%x", md5.Sum(rawDefaultCfg)),
}
isDefault, _ := am.isDefaultConfiguration(test.config)
raw, err := json.Marshal(test.config)
require.NoError(tt, err)
isDefault, _ := am.isDefaultConfiguration(md5.Sum(raw))
require.Equal(tt, test.expected, isDefault)
})
}
@@ -89,6 +89,7 @@ func TestLoader_Load(t *testing.T) {
},
Links: []plugins.InfoLink{
{Name: "Raise issue", URL: "https://github.com/grafana/grafana/issues/new"},
{Name: "Documentation", URL: "https://grafana.com/docs/grafana/latest/datasources/aws-cloudwatch/"},
},
},
Includes: []*plugins.Includes{
@@ -27,7 +27,7 @@ func ProvideDiscoveryStage(cfg *config.PluginManagementCfg, pf finder.Finder, pr
FindFunc: pf.Find,
FindFilterFuncs: []discovery.FindFilterFunc{
discovery.NewPermittedPluginTypesFilterStep([]plugins.Type{
plugins.TypeDataSource, plugins.TypeApp, plugins.TypePanel, plugins.TypeSecretsManager,
plugins.TypeDataSource, plugins.TypeApp, plugins.TypePanel,
}),
func(ctx context.Context, _ plugins.Class, b []*plugins.FoundBundle) ([]*plugins.FoundBundle, error) {
return NewDuplicatePluginIDFilterStep(pr).Filter(ctx, b)
@@ -70,7 +70,6 @@ var WireSet = wire.NewSet(
wire.Bind(new(pluginconfig.PluginRequestConfigProvider), new(*pluginconfig.RequestConfigProvider)),
pluginstore.ProvideService,
wire.Bind(new(pluginstore.Store), new(*pluginstore.Service)),
wire.Bind(new(plugins.SecretsPluginManager), new(*pluginstore.Service)),
wire.Bind(new(plugins.StaticRouteResolver), new(*pluginstore.Service)),
process.ProvideService,
wire.Bind(new(process.Manager), new(*process.Service)),
@@ -94,15 +94,6 @@ func (s *Service) Plugins(ctx context.Context, pluginTypes ...plugins.Type) []Pl
return pluginsList
}
func (s *Service) SecretsManager(ctx context.Context) *plugins.Plugin {
for _, p := range s.availablePlugins(ctx) {
if p.IsSecretsManager() {
return p
}
}
return nil
}
// plugin finds a plugin with `pluginID` from the registry that is not decommissioned
func (s *Service) plugin(ctx context.Context, pluginID string) (*plugins.Plugin, bool) {
p, exists := s.pluginRegistry.Plugin(ctx, pluginID, "") // version is not required since Grafana only supports single versions of a plugin
@@ -120,7 +120,6 @@ func TestStore_Routes(t *testing.T) {
t.Run("Routes returns all static routes for non-decommissioned plugins", func(t *testing.T) {
p1 := &plugins.Plugin{JSONData: plugins.JSONData{ID: "a-test-renderer", Type: plugins.TypeRenderer}, FS: fakes.NewFakePluginFS("/some/dir")}
p2 := &plugins.Plugin{JSONData: plugins.JSONData{ID: "b-test-panel", Type: plugins.TypePanel}, FS: fakes.NewFakePluginFS("/grafana/")}
p3 := &plugins.Plugin{JSONData: plugins.JSONData{ID: "c-test-secrets", Type: plugins.TypeSecretsManager}, FS: fakes.NewFakePluginFS("./secrets"), Class: plugins.ClassCore}
p4 := &plugins.Plugin{JSONData: plugins.JSONData{ID: "d-test-datasource", Type: plugins.TypeDataSource}, FS: fakes.NewFakePluginFS("../test")}
p5 := &plugins.Plugin{JSONData: plugins.JSONData{ID: "e-test-app", Type: plugins.TypeApp}, FS: fakes.NewFakePluginFS("any/path")}
p6 := &plugins.Plugin{JSONData: plugins.JSONData{ID: "f-test-app", Type: plugins.TypeApp}}
@@ -130,7 +129,6 @@ func TestStore_Routes(t *testing.T) {
Store: map[string]*plugins.Plugin{
p1.ID: p1,
p2.ID: p2,
p3.ID: p3,
p4.ID: p4,
p5.ID: p5,
p6.ID: p6,
@@ -146,27 +144,6 @@ func TestStore_Routes(t *testing.T) {
})
}
func TestStore_SecretsManager(t *testing.T) {
t.Run("Renderer returns a single (non-decommissioned) secrets manager plugin", func(t *testing.T) {
p1 := &plugins.Plugin{JSONData: plugins.JSONData{ID: "test-renderer", Type: plugins.TypeRenderer}}
p2 := &plugins.Plugin{JSONData: plugins.JSONData{ID: "test-panel", Type: plugins.TypePanel}}
p3 := &plugins.Plugin{JSONData: plugins.JSONData{ID: "test-secrets", Type: plugins.TypeSecretsManager}}
p4 := &plugins.Plugin{JSONData: plugins.JSONData{ID: "test-datasource", Type: plugins.TypeDataSource}}
ps := New(&fakes.FakePluginRegistry{
Store: map[string]*plugins.Plugin{
p1.ID: p1,
p2.ID: p2,
p3.ID: p3,
p4.ID: p4,
},
}, &fakes.FakeLoader{})
r := ps.SecretsManager(context.Background())
require.Equal(t, p3, r)
})
}
func TestProcessManager_shutdown(t *testing.T) {
p := &plugins.Plugin{JSONData: plugins.JSONData{ID: "test-datasource", Type: plugins.TypeDataSource}} // Backend: true
backend := &fakes.FakeBackendPlugin{}
@@ -39,7 +39,7 @@ func TestRenderer(t *testing.T) {
r, exists := m.Renderer(context.Background())
require.False(t, exists)
require.Equal(t, 4, numLoaded)
require.Equal(t, 3, numLoaded)
require.Equal(t, 0, numUnloaded)
require.Nil(t, r)
})
@@ -1,3 +0,0 @@
{
"type": "secretsmanager"
}
+1 -43
View File
@@ -5,13 +5,8 @@ import (
"time"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/kvstore"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin/secretsmanagerplugin"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/setting"
)
const (
@@ -22,45 +17,8 @@ const (
func ProvideService(
sqlStore db.DB,
secretsService secrets.Service,
pluginsManager plugins.SecretsPluginManager,
kvstore kvstore.KVStore,
features featuremgmt.FeatureToggles,
cfg *setting.Cfg,
) (SecretsKVStore, error) {
var logger = log.New("secrets.kvstore")
var store SecretsKVStore
ctx := context.Background()
store = NewSQLSecretsKVStore(sqlStore, secretsService, logger)
err := EvaluateRemoteSecretsPlugin(ctx, pluginsManager, cfg)
if err != nil {
logger.Debug("secrets manager evaluator returned false", "reason", err.Error())
} else {
// Attempt to start the plugin
var secretsPlugin secretsmanagerplugin.SecretsManagerPlugin
secretsPlugin, err = StartAndReturnPlugin(pluginsManager, ctx)
namespacedKVStore := GetNamespacedKVStore(kvstore)
if err != nil || secretsPlugin == nil {
logger.Error("failed to start remote secrets management plugin")
if isFatal, readErr := IsPluginStartupErrorFatal(ctx, namespacedKVStore); isFatal || readErr != nil {
// plugin error was fatal or there was an error determining if the error was fatal
logger.Error("secrets management plugin is required to start -- exiting app")
if readErr != nil {
return nil, readErr
}
return nil, err
}
} else {
// as the plugin is installed, SecretsKVStoreSQL is now replaced with
// an instance of SecretsKVStorePlugin with the sql store as a fallback
// (used for migration and in case a secret is not found).
store = NewPluginSecretsKVStore(secretsPlugin, secretsService, namespacedKVStore, features, WithCache(store, 5*time.Second, 5*time.Minute), logger)
}
}
if err != nil {
logger.Debug("secrets kvstore is using the default (SQL) implementation for secrets management")
}
store := NewSQLSecretsKVStore(sqlStore, secretsService, log.New("secrets.kvstore"))
return WithCache(store, 5*time.Second, 5*time.Minute), nil
}
@@ -1,108 +0,0 @@
package migrations
import (
"context"
"errors"
"fmt"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/kvstore"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin/secretsmanagerplugin"
"github.com/grafana/grafana/pkg/services/secrets"
secretskvs "github.com/grafana/grafana/pkg/services/secrets/kvstore"
"github.com/grafana/grafana/pkg/setting"
)
var errPluginUnavailable = errors.New("remote secret management plugin is unavailable")
// MigrateFromPluginService This migrator will handle migration of the configured plugin secrets back to Grafana unified secrets
type MigrateFromPluginService struct {
cfg *setting.Cfg
sqlStore db.DB
secretsService secrets.Service
manager plugins.SecretsPluginManager
kvstore kvstore.KVStore
}
func ProvideMigrateFromPluginService(
cfg *setting.Cfg,
sqlStore db.DB,
secretsService secrets.Service,
manager plugins.SecretsPluginManager,
kvstore kvstore.KVStore,
) *MigrateFromPluginService {
return &MigrateFromPluginService{
cfg: cfg,
sqlStore: sqlStore,
secretsService: secretsService,
manager: manager,
kvstore: kvstore,
}
}
func (s *MigrateFromPluginService) Migrate(ctx context.Context) error {
logger.Debug("starting migration of plugin secrets to unified secrets")
// access the plugin directly
plugin, err := secretskvs.StartAndReturnPlugin(s.manager, context.Background())
if err != nil {
return errPluginUnavailable
}
// Get full list of secrets from the plugin
res, err := plugin.GetAllSecrets(ctx, &secretsmanagerplugin.GetAllSecretsRequest{})
if err != nil {
logger.Error("Failed to retrieve all secrets from plugin")
return err
}
totalSecrets := len(res.Items)
logger.Debug("retrieved all secrets from plugin", "num secrets", totalSecrets)
// create a secret sql store manually
secretsSql := secretskvs.NewSQLSecretsKVStore(s.sqlStore, s.secretsService, logger)
for i, item := range res.Items {
logger.Debug(fmt.Sprintf("Migrating secret %d of %d", i+1, totalSecrets), "current", i+1, "secretCount", totalSecrets)
// Add to sql store
err = secretsSql.Set(ctx, item.Key.OrgId, item.Key.Namespace, item.Key.Type, item.Value)
if err != nil {
logger.Error("Error adding secret to unified secrets", "orgId", item.Key.OrgId,
"namespace", item.Key.Namespace, "type", item.Key.Type)
return err
}
}
for i, item := range res.Items {
logger.Debug(fmt.Sprintf("Cleaning secret %d of %d", i+1, totalSecrets), "current", i+1, "secretCount", totalSecrets)
// Delete from the plugin
_, err := plugin.DeleteSecret(ctx, &secretsmanagerplugin.DeleteSecretRequest{
KeyDescriptor: &secretsmanagerplugin.Key{
OrgId: item.Key.OrgId,
Namespace: item.Key.Namespace,
Type: item.Key.Type,
}})
if err != nil {
logger.Error("Error deleting secret from plugin after migration", "orgId", item.Key.OrgId,
"namespace", item.Key.Namespace, "type", item.Key.Type)
continue
}
}
logger.Debug("Completed migration of secrets from plugin")
// The plugin is no longer needed at the moment
err = secretskvs.SetPluginStartupErrorFatal(ctx, secretskvs.GetNamespacedKVStore(s.kvstore), false)
if err != nil {
logger.Error("Failed to remove plugin error fatal flag", "error", err.Error())
}
// Reset the fatal flag setter in case another secret is created on the plugin
secretskvs.ResetPlugin()
logger.Debug("Shutting down secrets plugin now that migration is complete")
// if `use_plugin` wasn't set, stop the plugin after migration
if !s.cfg.SectionWithEnvOverrides("secrets").Key("use_plugin").MustBool(false) {
err := s.manager.SecretsManager(ctx).Stop(ctx)
if err != nil {
// Log a warning but don't throw an error
logger.Error("Error stopping secrets plugin after migration", "error", err.Error())
}
}
return nil
}

Some files were not shown because too many files have changed in this diff Show More