mirror of
https://github.com/hashicorp/vault.git
synced 2026-06-04 06:15:02 -04:00
Create alias and command for OIDC (#6206)
This commit is contained in:
parent
95f9ba363e
commit
5cb9a8a5fa
20 changed files with 2044 additions and 264 deletions
|
|
@ -27,6 +27,7 @@ import (
|
|||
credAliCloud "github.com/hashicorp/vault-plugin-auth-alicloud"
|
||||
credCentrify "github.com/hashicorp/vault-plugin-auth-centrify"
|
||||
credGcp "github.com/hashicorp/vault-plugin-auth-gcp/plugin"
|
||||
credOIDC "github.com/hashicorp/vault-plugin-auth-jwt"
|
||||
credAws "github.com/hashicorp/vault/builtin/credential/aws"
|
||||
credCert "github.com/hashicorp/vault/builtin/credential/cert"
|
||||
credGitHub "github.com/hashicorp/vault/builtin/credential/github"
|
||||
|
|
@ -177,6 +178,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
"gcp": &credGcp.CLIHandler{},
|
||||
"github": &credGitHub.CLIHandler{},
|
||||
"ldap": &credLdap.CLIHandler{},
|
||||
"oidc": &credOIDC.CLIHandler{},
|
||||
"okta": &credOkta.CLIHandler{},
|
||||
"radius": &credUserpass.CLIHandler{
|
||||
DefaultMount: "radius",
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ func newRegistry() *registry {
|
|||
"jwt": credJWT.Factory,
|
||||
"kubernetes": credKube.Factory,
|
||||
"ldap": credLdap.Factory,
|
||||
"oidc": credJWT.Factory,
|
||||
"okta": credOkta.Factory,
|
||||
"radius": credRadius.Factory,
|
||||
"userpass": credUserpass.Factory,
|
||||
|
|
|
|||
201
vendor/github.com/hashicorp/vault-plugin-auth-jwt/Gopkg.lock
generated
vendored
201
vendor/github.com/hashicorp/vault-plugin-auth-jwt/Gopkg.lock
generated
vendored
|
|
@ -15,12 +15,12 @@
|
|||
version = "1.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:6bf6d532e503d9526d46e69aff04d11632c8c1e28b847dbd226babc1689aa723"
|
||||
digest = "1:c47f4964978e211c6e566596ec6246c329912ea92e9bb99c00798bb4564c5b09"
|
||||
name = "github.com/armon/go-radix"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "7fddfc383310abc091d79a27f116d30cf0424032"
|
||||
revision = "1a2de0c21c94309923825da3df33a4381872c795"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:f6e5e1bc64c2908167e6aa9a1fe0c084d515132a1c63ad5b6c84036aa06dc0c1"
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:17fe264ee908afc795734e8c4e63db2accabaf57326dbf21763a7d6b86096260"
|
||||
digest = "1:4c0989ca0bcd10799064318923b9bc2db6b4d6338dd75f3f2d86c3511aaaf5cf"
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = [
|
||||
"proto",
|
||||
|
|
@ -49,8 +49,8 @@
|
|||
"ptypes/timestamp",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
||||
version = "v1.1.0"
|
||||
revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
|
@ -62,106 +62,108 @@
|
|||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:d1971637b21871ec2033a44ca87c99c5608a7340cb34ec75fab8d2ab503276c9"
|
||||
digest = "1:0ade334594e69404d80d9d323445d2297ff8161637f9b2d347cc6973d2d6f05b"
|
||||
name = "github.com/hashicorp/errwrap"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "d6c0cd88035724dd42e0f335ae30161c20575ecc"
|
||||
revision = "8a6fb523712970c966eefc6b39ed2c5e74880354"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:77cb3be9b21ba7f1a4701e870c84ea8b66e7d74c7c8951c58155fdadae9414ec"
|
||||
digest = "1:f47d6109c2034cb16bd62b220e18afd5aa9d5a1630fe5d937ad96a4fb7cbb277"
|
||||
name = "github.com/hashicorp/go-cleanhttp"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "d5fe4b57a186c716b0e00b8c301cbd9b4182694d"
|
||||
revision = "e8ab9daed8d1ddd2d3c4efba338fe2eeae2e4f18"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:e8d99882caa8c74d68f340ddb9bba3f7e433117ce57c3e52501edfa7e195d2c7"
|
||||
digest = "1:0876aeb6edb07e20b6b0ce1d346655cb63dbe0a26ccfb47b68a9b7697709777b"
|
||||
name = "github.com/hashicorp/go-hclog"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "ff2cf002a8dd750586d91dddd4470c341f981fe1"
|
||||
revision = "4783caec6f2e5cdd47fab8b2bb47ce2ce5c546b7"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:2394f5a25132b3868eff44599cc28d44bdd0330806e34c495d754dd052df612b"
|
||||
digest = "1:2be5a35f0c5b35162c41bb24971e5dcf6ce825403296ee435429cdcc4e1e847e"
|
||||
name = "github.com/hashicorp/go-immutable-radix"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "7f3cd4390caab3250a57f30efdb2a65dd7649ecf"
|
||||
revision = "27df80928bb34bb1b0d6d0e01b9e679902e7a6b5"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:46fb6a9f1b9667f32ac93e08b1da118b2c666991424ea12e848b05d4fe5155ef"
|
||||
digest = "1:f668349b83f7d779567c880550534addeca7ebadfdcf44b0b9c39be61864b4b7"
|
||||
name = "github.com/hashicorp/go-multierror"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "3d5d8f294aa03d8e98859feac328afbdf1ae0703"
|
||||
revision = "886a7fbe3eb1c874d46f623bfa70af45f425b3d1"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:20f78c1cf1b6fe6c55ba1407350d6fc7dc77d1591f8106ba693c28014a1a1b37"
|
||||
digest = "1:77a6108b8eb3cd0feac4eeb3e032f36c8fdfe9497671952fd9eb682b9c503158"
|
||||
name = "github.com/hashicorp/go-plugin"
|
||||
packages = ["."]
|
||||
packages = [
|
||||
".",
|
||||
"internal/proto",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "a4620f9913d19f03a6bf19b2f304daaaf83ea130"
|
||||
revision = "362c99b11937c6a84686ee5726a8170e921ab406"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:183f00c472fb9b2446659618eebf4899872fa267b92f926539411abdc8b941df"
|
||||
digest = "1:d260503602063d71718eb21f85c02133ad5eac894c2a6f0e0546b7dc017dc97e"
|
||||
name = "github.com/hashicorp/go-retryablehttp"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "e651d75abec6fbd4f2c09508f72ae7af8a8b7171"
|
||||
revision = "73489d0a1476f0c9e6fb03f9c39241523a496dfd"
|
||||
version = "v0.5.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:45aad874d3c7d5e8610427c81870fb54970b981692930ec2a319ce4cb89d7a00"
|
||||
digest = "1:a54ada9beb59fdc35b69322979e870ff0b780e03f4dc309c4c8674b94927df75"
|
||||
name = "github.com/hashicorp/go-rootcerts"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "6bb64b370b90e7ef1fa532be9e591a81c3493e00"
|
||||
revision = "63503fb4e1eca22f9ae0f90b49c5d5538a0e87eb"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:14f2005c31ddf99c4a0f36fc440f8d1ac43224194c7c4a904b3c8f4ba5654d0b"
|
||||
digest = "1:3c4c27026ab6a3218dbde897568f651c81062e2ee6e617e57ae46ca95bb1db6b"
|
||||
name = "github.com/hashicorp/go-sockaddr"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "6d291a969b86c4b633730bfc6b8b9d64c3aafed9"
|
||||
revision = "3aed17b5ee41761cc2b04f2a94c7107d428967e5"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:354978aad16c56c27f57e5b152224806d87902e4935da3b03e18263d82ae77aa"
|
||||
digest = "1:f14364057165381ea296e49f8870a9ffce2b8a95e34d6ae06c759106aaef428c"
|
||||
name = "github.com/hashicorp/go-uuid"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "27454136f0364f2d44b1276c552d69105cf8c498"
|
||||
revision = "4f571afc59f3043a65f8fe6bf46d887b10a01d43"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:32c0e96a63bd093eccf37db757fb314be5996f34de93969321c2cbef893a7bd6"
|
||||
digest = "1:950caca7dfcf796419232ba996c9c3539d09f26af27ba848c4508e604c13efbb"
|
||||
name = "github.com/hashicorp/go-version"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "270f2f71b1ee587f3b609f00f422b76a6b28f348"
|
||||
revision = "d40cf49b3a77bba84a7afdbd7f1dc295d114efb1"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:cf296baa185baae04a9a7004efee8511d08e2f5f51d4cbe5375da89722d681db"
|
||||
digest = "1:8ec8d88c248041a6df5f6574b87bc00e7e0b493881dad2e7ef47b11dc69093b5"
|
||||
name = "github.com/hashicorp/golang-lru"
|
||||
packages = [
|
||||
".",
|
||||
"simplelru",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3"
|
||||
revision = "20f1fb78b0740ba8c3cb143a61e86ba5c8669768"
|
||||
version = "v0.5.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:12247a2e99a060cc692f6680e5272c8adf0b8f572e6bce0d7095e624c958a240"
|
||||
digest = "1:ea40c24cdbacd054a6ae9de03e62c5f252479b96c716375aace5c120d68647c8"
|
||||
name = "github.com/hashicorp/hcl"
|
||||
packages = [
|
||||
".",
|
||||
|
|
@ -175,11 +177,12 @@
|
|||
"json/token",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
|
||||
revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:d00de8725219a569ffbb5dd1042e4ced1f3b5ccee2b07218371f71026cc7609a"
|
||||
digest = "1:f5bdd7b0d06bfa965cefa9c52af7f556bd079ff4328d67c89f6afdf4be7eabbe"
|
||||
name = "github.com/hashicorp/vault"
|
||||
packages = [
|
||||
"api",
|
||||
|
|
@ -187,9 +190,11 @@
|
|||
"helper/cidrutil",
|
||||
"helper/compressutil",
|
||||
"helper/consts",
|
||||
"helper/cryptoutil",
|
||||
"helper/errutil",
|
||||
"helper/hclutil",
|
||||
"helper/jsonutil",
|
||||
"helper/license",
|
||||
"helper/locksutil",
|
||||
"helper/logging",
|
||||
"helper/mlock",
|
||||
|
|
@ -209,39 +214,47 @@
|
|||
"version",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "8655d167084028d627f687ddc25d0c71307eb5be"
|
||||
revision = "b16527d791ba46f74a608527b328957618aa0ae6"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:89658943622e6bc5e76b4da027ee9583fa0b321db0c797bd554edab96c1ca2b1"
|
||||
digest = "1:a4826c308e84f5f161b90b54a814f0be7d112b80164b9b884698a6903ea47ab3"
|
||||
name = "github.com/hashicorp/yamux"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "3520598351bb3500a49ae9563f5539666ae0a27c"
|
||||
revision = "2f1d1f20f75d5404f53b9edf6b53ed5505508675"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:c7354463195544b1ab3c1f1fadb41430947f5d28dfbf2cdbd38268c5717a5a03"
|
||||
digest = "1:5d231480e1c64a726869bc4142d270184c419749d34f167646baa21008eb0a79"
|
||||
name = "github.com/mitchellh/go-homedir"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "58046073cbffe2f25d425fe1331102f55cf719de"
|
||||
revision = "af06845cf3004701891bf4fdb884bfe4920b3727"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:cae1afe858922bd10e9573b87130f730a6e4183a00eba79920d6656629468bfa"
|
||||
digest = "1:42eb1f52b84a06820cedc9baec2e710bfbda3ee6dac6cdb97f8b9a5066134ec6"
|
||||
name = "github.com/mitchellh/go-testing-interface"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "a61a99592b77c9ba629d254a693acffaeb4b7e28"
|
||||
revision = "6d0b8010fcc857872e42fc6c931227569016843c"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:5ab79470a1d0fb19b041a624415612f8236b3c06070161a910562f2b2d064355"
|
||||
digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318"
|
||||
name = "github.com/mitchellh/mapstructure"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "f15292f7a699fcc1a38a80977f80a046874ba8ac"
|
||||
revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe"
|
||||
version = "v1.1.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:302de3c669b04a566d4e99760d6fb35a22177fc14c7a9284e8b3cf6e9fe3f28a"
|
||||
name = "github.com/mitchellh/pointerstructure"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "f2329fcfa9e280bdb5a3f2544aec815a508ad72f"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:9ec6cf1df5ad1d55cf41a43b6b1e7e118a91bade4f68ff4303379343e40c0e25"
|
||||
|
|
@ -251,6 +264,25 @@
|
|||
revision = "4dadeb3030eda0273a12382bb2348ffc7c9d1a39"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:808cdddf087fb64baeae67b8dfaee2069034d9704923a3cb8bd96a995421a625"
|
||||
name = "github.com/patrickmn/go-cache"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "a3647f8e31d79543b2d0f0ae2fe5c379d72cedc0"
|
||||
version = "v2.1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c7a5e79396b6eb570159df7a1d487ce5775bf43b7907976fbef6de544ea160ad"
|
||||
name = "github.com/pierrec/lz4"
|
||||
packages = [
|
||||
".",
|
||||
"internal/xxh32",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "473cd7ce01a1113208073166464b98819526150e"
|
||||
version = "v2.0.8"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:bd9efe4e0b0f768302a1e2f0c22458149278de533e521206e5ddc71848c269a0"
|
||||
|
|
@ -263,28 +295,29 @@
|
|||
revision = "1555304b9b35fdd2b425bccf1a5613677705e7d0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:0e792eea6c96ec55ff302ef33886acbaa5006e900fefe82689e88d96439dcd84"
|
||||
digest = "1:6baa565fe16f8657cf93469b2b8a6c61a277827734400d27e44d589547297279"
|
||||
name = "github.com/ryanuber/go-glob"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "572520ed46dbddaed19ea3d9541bdd0494163693"
|
||||
version = "v0.1"
|
||||
revision = "51a8f68e6c24dc43f1e371749c89a267de4ebc53"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:b8fa1ff0fc20983395978b3f771bb10438accbfe19326b02e236c1d4bf1c91b2"
|
||||
digest = "1:5bce6a1c0d1492cef01d74084ddbac09c4bbc4cbc1db3fdd0c138ed9bc945bf8"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = [
|
||||
"blake2b",
|
||||
"ed25519",
|
||||
"ed25519/internal/edwards25519",
|
||||
"pbkdf2",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "de0752318171da717af4ce24d0a2e8626afaeb11"
|
||||
revision = "193df9c0f06f8bb35fba505183eaf0acc0136505"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:3c4175c2711d67096567fc2d84a83464d6ff58119af3efc89983339d64144cb0"
|
||||
digest = "1:9d2f08c64693fbe7177b5980f80c35672c80f12be79bb3bc86948b934d70e4ee"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"context",
|
||||
|
|
@ -297,26 +330,29 @@
|
|||
"trace",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "aaf60122140d3fcf75376d319f0554393160eb50"
|
||||
revision = "65e2d4e15006aab9813ff8769e768bbf4bb667a0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:af19f6e6c369bf51ef226e989034cd88a45083173c02ac4d7ab74c9a90d356b7"
|
||||
digest = "1:e007b54f54cbd4214aa6d97a67d57bc2539991adb4e22ea92c482bbece8de469"
|
||||
name = "golang.org/x/oauth2"
|
||||
packages = [
|
||||
".",
|
||||
"internal",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "3d292e4d0cdc3a0113e6d207bb137145ef1de42f"
|
||||
revision = "99b60b757ec124ebb7d6b7e97f153b19c10ce163"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:05662433b3a13c921587a6e622b5722072edff83211efd1cd79eeaeedfd83f07"
|
||||
digest = "1:c9e49928119661a681af4037236af47654d6bd421c0af184962c890d0a61e0fb"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
packages = [
|
||||
"cpu",
|
||||
"unix",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "1c9583448a9c3aa0f9a6a5241bf73c0bd8aafded"
|
||||
revision = "3b5209105503162ded1863c307ac66fec31120dd"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
|
||||
|
|
@ -343,14 +379,14 @@
|
|||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:c9e7a4b4d47c0ed205d257648b0e5b0440880cb728506e318f8ac7cd36270bc4"
|
||||
digest = "1:9fdc2b55e8e0fafe4b41884091e51e77344f7dc511c5acedcfd98200003bff90"
|
||||
name = "golang.org/x/time"
|
||||
packages = ["rate"]
|
||||
pruneopts = "UT"
|
||||
revision = "fbb02b2291d28baffd63558aa44b4b56f178d650"
|
||||
revision = "85acf8d2951cb2a3bde7632f9ff273ef0379bcbd"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:328b5e4f197d928c444a51a75385f4b978915c0e75521f0ad6a3db976c97a7d3"
|
||||
digest = "1:6f3bd49ddf2e104e52062774d797714371fac1b8bddfd8e124ce78e6b2264a10"
|
||||
name = "google.golang.org/appengine"
|
||||
packages = [
|
||||
"internal",
|
||||
|
|
@ -362,8 +398,8 @@
|
|||
"urlfetch",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "b1f26356af11148e710935ed1ac8a7f5702c7612"
|
||||
version = "v1.1.0"
|
||||
revision = "e9657d882bb81064595ca3b56cbe2546bbabf7b1"
|
||||
version = "v1.4.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
|
@ -371,19 +407,21 @@
|
|||
name = "google.golang.org/genproto"
|
||||
packages = ["googleapis/rpc/status"]
|
||||
pruneopts = "UT"
|
||||
revision = "d0a8f471bba2dbb160885b0000d814ee5d559bad"
|
||||
revision = "4b09977fb92221987e99d190c8f88f2c92727a29"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:047efbc3c9a51f3002b0002f92543857d372654a676fb6b01931982cd80467dd"
|
||||
digest = "1:a887a56d0ff92cf05b4bb6004b46fc6e64d3fb6aca4eaeb1466bdce183ba5004"
|
||||
name = "google.golang.org/grpc"
|
||||
packages = [
|
||||
".",
|
||||
"balancer",
|
||||
"balancer/base",
|
||||
"balancer/roundrobin",
|
||||
"binarylog/grpc_binarylog_v1",
|
||||
"codes",
|
||||
"connectivity",
|
||||
"credentials",
|
||||
"credentials/internal",
|
||||
"encoding",
|
||||
"encoding/proto",
|
||||
"grpclog",
|
||||
|
|
@ -391,9 +429,12 @@
|
|||
"health/grpc_health_v1",
|
||||
"internal",
|
||||
"internal/backoff",
|
||||
"internal/binarylog",
|
||||
"internal/channelz",
|
||||
"internal/envconfig",
|
||||
"internal/grpcrand",
|
||||
"internal/grpcsync",
|
||||
"internal/syscall",
|
||||
"internal/transport",
|
||||
"keepalive",
|
||||
"metadata",
|
||||
|
|
@ -407,11 +448,11 @@
|
|||
"tap",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "32fb0ac620c32ba40a4626ddf94d90d12cce3455"
|
||||
version = "v1.14.0"
|
||||
revision = "a02b0774206b209466313a0b525d2c738fe407eb"
|
||||
version = "v1.18.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:b57bb9a6a2a03558d63166f1afc3c0c4f91ad137f63bf2bee995e9baeb976a9c"
|
||||
digest = "1:a4cde1eec9a17eb2399a50c6e1a9fe3fde039994de058f9dbf6592d157bfe97b"
|
||||
name = "gopkg.in/square/go-jose.v2"
|
||||
packages = [
|
||||
".",
|
||||
|
|
@ -420,8 +461,8 @@
|
|||
"jwt",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "8254d6c783765f38c8675fae4427a1fe73fbd09d"
|
||||
version = "v2.1.8"
|
||||
revision = "e94fb177d3668d35ab39c61cbb2f311550557e83"
|
||||
version = "v2.2.2"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
|
|
@ -433,6 +474,8 @@
|
|||
"github.com/hashicorp/go-cleanhttp",
|
||||
"github.com/hashicorp/go-hclog",
|
||||
"github.com/hashicorp/go-sockaddr",
|
||||
"github.com/hashicorp/go-uuid",
|
||||
"github.com/hashicorp/vault/api",
|
||||
"github.com/hashicorp/vault/helper/certutil",
|
||||
"github.com/hashicorp/vault/helper/cidrutil",
|
||||
"github.com/hashicorp/vault/helper/logging",
|
||||
|
|
@ -443,6 +486,8 @@
|
|||
"github.com/hashicorp/vault/logical",
|
||||
"github.com/hashicorp/vault/logical/framework",
|
||||
"github.com/hashicorp/vault/logical/plugin",
|
||||
"github.com/mitchellh/pointerstructure",
|
||||
"github.com/patrickmn/go-cache",
|
||||
"golang.org/x/oauth2",
|
||||
"gopkg.in/square/go-jose.v2",
|
||||
"gopkg.in/square/go-jose.v2/jwt",
|
||||
|
|
|
|||
13
vendor/github.com/hashicorp/vault-plugin-auth-jwt/backend.go
generated
vendored
13
vendor/github.com/hashicorp/vault-plugin-auth-jwt/backend.go
generated
vendored
|
|
@ -3,10 +3,12 @@ package jwtauth
|
|||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
oidc "github.com/coreos/go-oidc"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
cache "github.com/patrickmn/go-cache"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -16,7 +18,7 @@ const (
|
|||
|
||||
// Factory is used by framework
|
||||
func Factory(ctx context.Context, c *logical.BackendConfig) (logical.Backend, error) {
|
||||
b := backend(c)
|
||||
b := backend()
|
||||
if err := b.Setup(ctx, c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -29,14 +31,16 @@ type jwtAuthBackend struct {
|
|||
l sync.RWMutex
|
||||
provider *oidc.Provider
|
||||
cachedConfig *jwtConfig
|
||||
oidcStates *cache.Cache
|
||||
|
||||
providerCtx context.Context
|
||||
providerCtxCancel context.CancelFunc
|
||||
}
|
||||
|
||||
func backend(c *logical.BackendConfig) *jwtAuthBackend {
|
||||
func backend() *jwtAuthBackend {
|
||||
b := new(jwtAuthBackend)
|
||||
b.providerCtx, b.providerCtxCancel = context.WithCancel(context.Background())
|
||||
b.oidcStates = cache.New(oidcStateTimeout, 1*time.Minute)
|
||||
|
||||
b.Backend = &framework.Backend{
|
||||
AuthRenew: b.pathLoginRenew,
|
||||
|
|
@ -46,6 +50,9 @@ func backend(c *logical.BackendConfig) *jwtAuthBackend {
|
|||
PathsSpecial: &logical.Paths{
|
||||
Unauthenticated: []string{
|
||||
"login",
|
||||
"oidc/auth_url",
|
||||
"oidc/callback",
|
||||
"ui", // TODO: remove when Vault UI is ready
|
||||
},
|
||||
SealWrapStorage: []string{
|
||||
"config",
|
||||
|
|
@ -57,7 +64,9 @@ func backend(c *logical.BackendConfig) *jwtAuthBackend {
|
|||
pathRoleList(b),
|
||||
pathRole(b),
|
||||
pathConfig(b),
|
||||
pathUI(b), // TODO: remove when Vault UI is ready
|
||||
},
|
||||
pathOIDC(b),
|
||||
),
|
||||
Clean: b.cleanup,
|
||||
}
|
||||
|
|
|
|||
65
vendor/github.com/hashicorp/vault-plugin-auth-jwt/claims.go
generated
vendored
Normal file
65
vendor/github.com/hashicorp/vault-plugin-auth-jwt/claims.go
generated
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package jwtauth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
"github.com/mitchellh/pointerstructure"
|
||||
)
|
||||
|
||||
// getClaim returns a claim value from allClaims given a provided claim string.
|
||||
// If this string is a valid JSONPointer, it will be interpreted as such to locate
|
||||
// the claim. Otherwise, the claim string will be used directly.
|
||||
func getClaim(logger log.Logger, allClaims map[string]interface{}, claim string) interface{} {
|
||||
var val interface{}
|
||||
var err error
|
||||
|
||||
if !strings.HasPrefix(claim, "/") {
|
||||
val = allClaims[claim]
|
||||
} else {
|
||||
val, err = pointerstructure.Get(allClaims, claim)
|
||||
if err != nil {
|
||||
logger.Warn(fmt.Sprintf("unable to locate %s in claims: %s", claim, err.Error()))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// The claims unmarshalled by go-oidc don't use UseNumber, so there will
|
||||
// be mismatches if they're coming in as float64 since Vault's config will
|
||||
// be represented as json.Number. If the operator can coerce claims data to
|
||||
// be in string form, there is no problem. Alternatively, we could try to
|
||||
// intelligently convert float64 to json.Number, e.g.:
|
||||
//
|
||||
// switch v := val.(type) {
|
||||
// case float64:
|
||||
// val = json.Number(strconv.Itoa(int(v)))
|
||||
// }
|
||||
//
|
||||
// Or we fork and/or PR go-oidc.
|
||||
|
||||
return val
|
||||
}
|
||||
|
||||
// extractMetadata builds a metadata map from a set of claims and claims mappings.
|
||||
// The referenced claims must be strings and the claims mappings must be of the structure:
|
||||
//
|
||||
// {
|
||||
// "/some/claim/pointer": "metadata_key1",
|
||||
// "another_claim": "metadata_key2",
|
||||
// ...
|
||||
// }
|
||||
func extractMetadata(logger log.Logger, allClaims map[string]interface{}, claimMappings map[string]string) (map[string]string, error) {
|
||||
metadata := make(map[string]string)
|
||||
for source, target := range claimMappings {
|
||||
if value := getClaim(logger, allClaims, source); value != nil {
|
||||
strValue, ok := value.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("error converting claim '%s' to string", source)
|
||||
}
|
||||
|
||||
metadata[target] = strValue
|
||||
}
|
||||
}
|
||||
return metadata, nil
|
||||
}
|
||||
502
vendor/github.com/hashicorp/vault-plugin-auth-jwt/cli.go
generated
vendored
Normal file
502
vendor/github.com/hashicorp/vault-plugin-auth-jwt/cli.go
generated
vendored
Normal file
|
|
@ -0,0 +1,502 @@
|
|||
package jwtauth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/vault/api"
|
||||
)
|
||||
|
||||
const defaultMount = "oidc"
|
||||
const defaultPort = "8300"
|
||||
|
||||
type CLIHandler struct{}
|
||||
|
||||
type loginResp struct {
|
||||
secret *api.Secret
|
||||
err error
|
||||
}
|
||||
|
||||
func (h *CLIHandler) Auth(c *api.Client, m map[string]string) (*api.Secret, error) {
|
||||
// handle ctrl-c while waiting for the callback
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, os.Interrupt)
|
||||
defer signal.Stop(ch)
|
||||
|
||||
doneCh := make(chan loginResp)
|
||||
|
||||
mount, ok := m["mount"]
|
||||
if !ok {
|
||||
mount = defaultMount
|
||||
}
|
||||
|
||||
port, ok := m["port"]
|
||||
if !ok {
|
||||
port = defaultPort
|
||||
}
|
||||
|
||||
role := m["role"]
|
||||
if role == "" {
|
||||
return nil, errors.New("a 'role' must be specified")
|
||||
}
|
||||
|
||||
secret, err := fetchAuthURL(c, role, mount, port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authURL := secret.Data["auth_url"].(string)
|
||||
if authURL == "" {
|
||||
return nil, errors.New(fmt.Sprintf("Unable to authorize role %q. Check Vault logs for more information.", role))
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Complete the login via your OIDC provider. Launching browser to:\n\n %s\n\n\n", authURL)
|
||||
if err := openURL(authURL); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error attempting to automatically open browser: '%s'.\nPlease visit the authorization URL manually.", err)
|
||||
}
|
||||
|
||||
// Set up callback handler
|
||||
http.HandleFunc(fmt.Sprintf("/v1/auth/%s/oidc/callback", mount), func(w http.ResponseWriter, req *http.Request) {
|
||||
var response string
|
||||
|
||||
query := req.URL.Query()
|
||||
code := query.Get("code")
|
||||
state := query.Get("state")
|
||||
data := map[string][]string{
|
||||
"code": {code},
|
||||
"state": {state},
|
||||
}
|
||||
|
||||
secret, err := c.Logical().ReadWithData(fmt.Sprintf("auth/%s/oidc/callback", mount), data)
|
||||
if err != nil {
|
||||
summary, detail := parseError(err)
|
||||
response = errorHTML(summary, detail)
|
||||
} else {
|
||||
response = successHTML
|
||||
}
|
||||
|
||||
w.Write([]byte(response))
|
||||
doneCh <- loginResp{secret, err}
|
||||
})
|
||||
|
||||
// Start local server
|
||||
go func() {
|
||||
if err := http.ListenAndServe(":"+port, nil); err != nil && err != http.ErrServerClosed {
|
||||
fmt.Fprintf(os.Stderr, "Error listening for callback: %v\n\n", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for either the callback to finish or SIGINT to be received
|
||||
select {
|
||||
case s := <-doneCh:
|
||||
return s.secret, s.err
|
||||
case <-ch:
|
||||
return nil, errors.New("interrupted")
|
||||
}
|
||||
}
|
||||
|
||||
func fetchAuthURL(c *api.Client, role, mount, port string) (*api.Secret, error) {
|
||||
data := map[string]interface{}{
|
||||
"role": role,
|
||||
"redirect_uri": fmt.Sprintf("http://localhost:%s/v1/auth/%s/oidc/callback", port, mount),
|
||||
}
|
||||
|
||||
return c.Logical().Write(fmt.Sprintf("auth/%s/oidc/auth_url", mount), data)
|
||||
}
|
||||
|
||||
// openURL opens the specified URL in the default browser of the user.
|
||||
// Source: https://stackoverflow.com/a/39324149/453290
|
||||
func openURL(url string) error {
|
||||
var cmd string
|
||||
var args []string
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
cmd = "cmd"
|
||||
args = []string{"/c", "start"}
|
||||
case "darwin":
|
||||
cmd = "open"
|
||||
default: // "linux", "freebsd", "openbsd", "netbsd"
|
||||
cmd = "xdg-open"
|
||||
}
|
||||
args = append(args, url)
|
||||
return exec.Command(cmd, args...).Start()
|
||||
}
|
||||
|
||||
// parseError converts error from the API into summary and detailed portions.
|
||||
func parseError(err error) (string, string) {
|
||||
headers := []string{errNoResponse, errLoginFailed, errTokenVerification}
|
||||
summary := "Login error"
|
||||
detail := ""
|
||||
|
||||
re := regexp.MustCompile(`(?s)Errors:.*\* *(.*)`)
|
||||
|
||||
errorParts := re.FindStringSubmatch(err.Error())
|
||||
switch len(errorParts) {
|
||||
case 0:
|
||||
summary = ""
|
||||
case 1:
|
||||
detail = errorParts[0]
|
||||
case 2:
|
||||
for _, h := range headers {
|
||||
if strings.HasPrefix(errorParts[1], h) {
|
||||
summary = h
|
||||
detail = strings.TrimSpace(errorParts[1][len(h):])
|
||||
break
|
||||
}
|
||||
}
|
||||
if detail == "" {
|
||||
detail = errorParts[1]
|
||||
}
|
||||
}
|
||||
|
||||
return summary, detail
|
||||
|
||||
}
|
||||
|
||||
func errorHTML(summary, detail string) string {
|
||||
const html = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" >
|
||||
|
||||
<head>
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>HashiCorp Vault</title>
|
||||
<style>
|
||||
body {
|
||||
font-size: 14px;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||
"Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
|
||||
"Helvetica Neue", sans-serif;
|
||||
}
|
||||
hr {
|
||||
border-color: #fdfdfe;
|
||||
margin: 24px 0;
|
||||
}
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 70vh;
|
||||
}
|
||||
#logo {
|
||||
display: block;
|
||||
fill: #6f7682;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.message {
|
||||
display: flex;
|
||||
min-width: 40vw;
|
||||
background: #fafdfa;
|
||||
border: 1px solid #c6e9c9;
|
||||
margin-bottom: 12px;
|
||||
padding: 12px 16px 16px 12px;
|
||||
position: relative;
|
||||
border-radius: 2px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.message.is-danger {
|
||||
background: #fdfafb;
|
||||
border-color: #f9ecee;
|
||||
}
|
||||
.message-content {
|
||||
margin-left: 4px;
|
||||
}
|
||||
.message svg {
|
||||
fill: #2eb039;
|
||||
}
|
||||
|
||||
.message.is-danger svg {
|
||||
fill: #c73445;
|
||||
}
|
||||
.message .message-title {
|
||||
color: #1e7125;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 1.25;
|
||||
}
|
||||
.message.is-danger .message-title {
|
||||
color: #7f222c;
|
||||
|
||||
}
|
||||
.message .message-body {
|
||||
border: 0;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.message p {
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: #17421b;
|
||||
}
|
||||
.message.is-danger p {
|
||||
color: #1f2124;
|
||||
}
|
||||
a {
|
||||
display: block;
|
||||
margin: 8px 0;
|
||||
color: #1563ff;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
a:hover {
|
||||
color: black;
|
||||
}
|
||||
a svg {
|
||||
fill: currentcolor;
|
||||
}
|
||||
.icon {
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
height: 21px;
|
||||
width: 21px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 17.5px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h1 + p {
|
||||
margin: 8px 0 16px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body translate="no" >
|
||||
<div class="container">
|
||||
<div>
|
||||
<svg id="logo" width="146" height="51" viewBox="0 0 146 51" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="vault-logo-v" fill-rule="nonzero">
|
||||
<path d="M0,0 L25.4070312,51 L51,0 L0,0 Z M28.5,10.5 L31.5,10.5 L31.5,13.5 L28.5,13.5 L28.5,10.5 Z M22.5,22.5 L19.5,22.5 L19.5,19.5 L22.5,19.5 L22.5,22.5 Z M22.5,18 L19.5,18 L19.5,15 L22.5,15 L22.5,18 Z M22.5,13.5 L19.5,13.5 L19.5,10.5 L22.5,10.5 L22.5,13.5 Z M26.991018,27 L24,27 L24,24 L27,24 L26.991018,27 Z M26.991018,22.5 L24,22.5 L24,19.5 L27,19.5 L26.991018,22.5 Z M26.991018,18 L24,18 L24,15 L27,15 L26.991018,18 Z M26.991018,13.5 L24,13.5 L24,10.5 L27,10.5 L26.991018,13.5 Z M28.5,15 L31.5,15 L31.5,18 L28.5089552,18 L28.5,15 Z M28.5,22.5 L28.5,19.5 L31.5,19.5 L31.5,22.4601182 L28.5,22.5 Z"></path>
|
||||
</g>
|
||||
<path id="vault-logo-name" d="M69.7218638,30.2482468 L63.2587814,8.45301543 L58,8.45301543 L65.9885305,34.6072931 L73.4551971,34.6072931 L81.4437276,8.45301543 L76.1849462,8.45301543 L69.7218638,30.2482468 Z M97.6329749,22.0014025 C97.6329749,17.2103787 95.8265233,15.0897616 89.6845878,15.0897616 C87.5168459,15.0897616 84.8272401,15.4431978 82.9806452,15.9929874 L83.5827957,19.6451613 C85.3089606,19.2917251 87.2358423,19.056101 89.0021505,19.056101 C92.1333333,19.056101 92.7354839,19.802244 92.7354839,21.9228612 L92.7354839,23.9256662 L88.0387097,23.9256662 C84.0645161,23.9256662 82.3383513,25.4179523 82.3383513,29.3057504 C82.3383513,32.6044881 83.8637993,35 87.4365591,35 C89.4035842,35 91.4910394,34.4502104 93.2573477,33.3113604 L93.618638,34.6072931 L97.6329749,34.6072931 L97.6329749,22.0014025 Z M92.7354839,30.2089762 C91.8121864,30.7194951 90.4874552,31.1907433 89.0422939,31.1907433 C87.5168459,31.1907433 87.0752688,30.601683 87.0752688,29.2664797 C87.0752688,27.8134642 87.5168459,27.3814867 89.1225806,27.3814867 L92.7354839,27.3814867 L92.7354839,30.2089762 Z M102.421505,15.4824684 L102.421505,29.345021 C102.421505,32.7615708 103.585663,35 106.837276,35 C109.125448,35 112.216487,34.1753156 114.665233,32.997195 L115.146953,34.6072931 L118.880287,34.6072931 L118.880287,15.4824684 L113.982796,15.4824684 L113.982796,28.7559607 C112.216487,29.6591865 110.088889,30.3660589 108.884588,30.3660589 C107.760573,30.3660589 107.318996,29.85554 107.318996,28.8345021 L107.318996,15.4824684 L102.421505,15.4824684 Z M129.168459,34.6072931 L129.168459,7 L124.270968,7.66760168 L124.270968,34.6072931 L129.168459,34.6072931 Z M144.394265,30.601683 C143.551254,30.8373072 142.6681,30.9943899 141.94552,30.9943899 C140.660932,30.9943899 140.179211,30.3267882 140.179211,29.3057504 L140.179211,19.2917251 L144.875986,19.2917251 L145.197133,15.4824684 L140.179211,15.4824684 L140.179211,10.0631136 L135.28172,10.7307153 L135.28172,15.4824684 L132.351254,15.4824684 L132.351254,19.2917251 L135.28172,19.2917251 L135.28172,29.9340813 C135.28172,33.3506311 137.088172,35 140.660932,35 C141.905376,35 143.912545,34.6858345 144.956272,34.2538569 L144.394265,30.601683 Z"></path>
|
||||
</svg>
|
||||
<div class="message is-danger">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<path d="M19 3c1.1 0 2 .9 2 2v14c0 1.1-.9 2-2 2H5c-1.1 0-2-.9-2-2V5c0-1.1.9-2 2-2h14zm-2 12.59L13.41 12 17 8.41 15.59 7 12 10.59 8.41 7 7 8.41 10.59 12 7 15.59 8.41 17 12 13.41 15.59 17 17 15.59z"></path>
|
||||
</svg>
|
||||
<div class="message-content">
|
||||
<div class="message-title">
|
||||
%s
|
||||
</div>
|
||||
<p class="message-body">
|
||||
%s
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
<h1>Not sure how to get started?</h1>
|
||||
<p class="learn">
|
||||
Check out beginner and advanced guides on HashiCorp Vault at the HashiCorp Learn site or read more in the official documentation.
|
||||
</p>
|
||||
<a href="https://learn.hashicorp.com/vault" rel="noreferrer noopener">
|
||||
<span class="icon">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.338 2.255a.79.79 0 0 0-.645 0L.657 5.378c-.363.162-.534.538-.534.875 0 .337.171.713.534.875l1.436.637c-.332.495-.638 1.18-.744 2.106a.887.887 0 0 0-.26 1.559c.02.081.03.215.013.392-.02.205-.074.43-.162.636-.186.431-.45.64-.741.64v.98c.651 0 1.108-.365 1.403-.797l.06.073c.32.372.826.763 1.455.763v-.98c-.215 0-.474-.145-.71-.42-.111-.13-.2-.27-.259-.393a1.014 1.014 0 0 1-.06-.155c-.01-.036-.013-.055-.013-.058h-.022a2.544 2.544 0 0 0 .031-.641.886.886 0 0 0-.006-1.51c.1-.868.398-1.477.699-1.891l.332.147-.023.746v2.228c0 .115.04.22.105.304.124.276.343.5.587.677.297.217.675.396 1.097.54.846.288 1.943.456 3.127.456 1.185 0 2.281-.168 3.128-.456.422-.144.8-.323 1.097-.54.244-.177.462-.401.586-.677a.488.488 0 0 0 .106-.304V8.218l2.455-1.09c.363-.162.534-.538.534-.875 0-.337-.17-.713-.534-.875L8.338 2.255zm-.34 2.955L3.64 7.38l4.375 1.942 6.912-3.069-6.912-3.07-6.912 3.07 1.665.74 4.901-2.44.328.657zM14.307 1H12.5a.5.5 0 1 1 0-1h3a.499.499 0 0 1 .5.65V3.5a.5.5 0 1 1-1 0V1.72l-1.793 1.774a.5.5 0 0 1-.713-.701L14.307 1zm-2.368 7.653v2.383a.436.436 0 0 0-.007.021c-.017.063-.084.178-.282.322-.193.14-.473.28-.836.404-.724.247-1.71.404-2.812.404-1.1 0-2.087-.157-2.811-.404a3.188 3.188 0 0 1-.836-.404c-.198-.144-.265-.26-.282-.322a.437.437 0 0 0-.007-.02V8.983l.01-.338 3.617 1.605a.791.791 0 0 0 .645 0l3.6-1.598z" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</span>
|
||||
Get started with Vault
|
||||
</a>
|
||||
|
||||
|
||||
<a href="https://vaultproject.io/docs" rel="noreferrer noopener">
|
||||
<span class="icon">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.307 1H11.5a.5.5 0 1 1 0-1h3a.499.499 0 0 1 .5.65V3.5a.5.5 0 1 1-1 0V1.72l-1.793 1.774a.5.5 0 0 1-.713-.701L13.307 1zM12 14V8a.5.5 0 1 1 1 0v6.5a.5.5 0 0 1-.5.5H.563a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 .5-.5H8a.5.5 0 0 1 0 1H1v12h11zM4 6a.5.5 0 0 1 0-1h3a.5.5 0 0 1 0 1H4zm0 2.5a.5.5 0 0 1 0-1h5a.5.5 0 0 1 0 1H4zM4 11a.5.5 0 1 1 0-1h5a.5.5 0 1 1 0 1H4z"/>
|
||||
</svg>
|
||||
</span>
|
||||
View the official Vault documentation
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
`
|
||||
return fmt.Sprintf(html, summary, detail)
|
||||
}
|
||||
|
||||
// Help method for OIDC cli
|
||||
func (h *CLIHandler) Help() string {
|
||||
help := `
|
||||
Usage: vault login -method=oidc [CONFIG K=V...]
|
||||
|
||||
The OIDC auth method allows users to authenticate using an OIDC provider.
|
||||
The provider must be configured as part of a role by the operator.
|
||||
|
||||
Authenticate using role "engineering":
|
||||
|
||||
$ vault login -method=oidc role=engineering
|
||||
Complete the login via your OIDC provider. Launching browser to:
|
||||
|
||||
https://accounts.google.com/o/oauth2/v2/...
|
||||
|
||||
The default browser will be opened for the user to complete the login. Alternatively,
|
||||
the user may visit the provided URL directly.
|
||||
|
||||
Configuration:
|
||||
|
||||
role=<string>
|
||||
Vault role of type "OIDC" to use for authentication.
|
||||
|
||||
port=<string>
|
||||
Optional localhost port to use for OIDC callback (default: 8300).
|
||||
`
|
||||
|
||||
return strings.TrimSpace(help)
|
||||
}
|
||||
|
||||
const successHTML = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Vault Authentication Succeeded</title>
|
||||
<style>
|
||||
body {
|
||||
font-size: 14px;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||
"Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
|
||||
"Helvetica Neue", sans-serif;
|
||||
}
|
||||
hr {
|
||||
border-color: #fdfdfe;
|
||||
margin: 24px 0;
|
||||
}
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 70vh;
|
||||
}
|
||||
#logo {
|
||||
display: block;
|
||||
fill: #6f7682;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.message {
|
||||
display: flex;
|
||||
min-width: 40vw;
|
||||
background: #fafdfa;
|
||||
border: 1px solid #c6e9c9;
|
||||
margin-bottom: 12px;
|
||||
padding: 12px 16px 16px 12px;
|
||||
position: relative;
|
||||
border-radius: 2px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.message-content {
|
||||
margin-left: 4px;
|
||||
}
|
||||
.message #checkbox {
|
||||
fill: #2eb039;
|
||||
}
|
||||
.message .message-title {
|
||||
color: #1e7125;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 1.25;
|
||||
}
|
||||
.message .message-body {
|
||||
border: 0;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.message p {
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: #17421b;
|
||||
}
|
||||
a {
|
||||
display: block;
|
||||
margin: 8px 0;
|
||||
color: #1563ff;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
a:hover {
|
||||
color: black;
|
||||
}
|
||||
a svg {
|
||||
fill: currentcolor;
|
||||
}
|
||||
.icon {
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
height: 21px;
|
||||
width: 21px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
h1 {
|
||||
font-size: 17.5px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
h1 + p {
|
||||
margin: 8px 0 16px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body translate="no" >
|
||||
<div class="container">
|
||||
<div>
|
||||
<svg id="logo" width="146" height="51" viewBox="0 0 146 51" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="vault-logo-v" fill-rule="nonzero">
|
||||
<path d="M0,0 L25.4070312,51 L51,0 L0,0 Z M28.5,10.5 L31.5,10.5 L31.5,13.5 L28.5,13.5 L28.5,10.5 Z M22.5,22.5 L19.5,22.5 L19.5,19.5 L22.5,19.5 L22.5,22.5 Z M22.5,18 L19.5,18 L19.5,15 L22.5,15 L22.5,18 Z M22.5,13.5 L19.5,13.5 L19.5,10.5 L22.5,10.5 L22.5,13.5 Z M26.991018,27 L24,27 L24,24 L27,24 L26.991018,27 Z M26.991018,22.5 L24,22.5 L24,19.5 L27,19.5 L26.991018,22.5 Z M26.991018,18 L24,18 L24,15 L27,15 L26.991018,18 Z M26.991018,13.5 L24,13.5 L24,10.5 L27,10.5 L26.991018,13.5 Z M28.5,15 L31.5,15 L31.5,18 L28.5089552,18 L28.5,15 Z M28.5,22.5 L28.5,19.5 L31.5,19.5 L31.5,22.4601182 L28.5,22.5 Z"></path>
|
||||
</g>
|
||||
<path id="vault-logo-name" d="M69.7218638,30.2482468 L63.2587814,8.45301543 L58,8.45301543 L65.9885305,34.6072931 L73.4551971,34.6072931 L81.4437276,8.45301543 L76.1849462,8.45301543 L69.7218638,30.2482468 Z M97.6329749,22.0014025 C97.6329749,17.2103787 95.8265233,15.0897616 89.6845878,15.0897616 C87.5168459,15.0897616 84.8272401,15.4431978 82.9806452,15.9929874 L83.5827957,19.6451613 C85.3089606,19.2917251 87.2358423,19.056101 89.0021505,19.056101 C92.1333333,19.056101 92.7354839,19.802244 92.7354839,21.9228612 L92.7354839,23.9256662 L88.0387097,23.9256662 C84.0645161,23.9256662 82.3383513,25.4179523 82.3383513,29.3057504 C82.3383513,32.6044881 83.8637993,35 87.4365591,35 C89.4035842,35 91.4910394,34.4502104 93.2573477,33.3113604 L93.618638,34.6072931 L97.6329749,34.6072931 L97.6329749,22.0014025 Z M92.7354839,30.2089762 C91.8121864,30.7194951 90.4874552,31.1907433 89.0422939,31.1907433 C87.5168459,31.1907433 87.0752688,30.601683 87.0752688,29.2664797 C87.0752688,27.8134642 87.5168459,27.3814867 89.1225806,27.3814867 L92.7354839,27.3814867 L92.7354839,30.2089762 Z M102.421505,15.4824684 L102.421505,29.345021 C102.421505,32.7615708 103.585663,35 106.837276,35 C109.125448,35 112.216487,34.1753156 114.665233,32.997195 L115.146953,34.6072931 L118.880287,34.6072931 L118.880287,15.4824684 L113.982796,15.4824684 L113.982796,28.7559607 C112.216487,29.6591865 110.088889,30.3660589 108.884588,30.3660589 C107.760573,30.3660589 107.318996,29.85554 107.318996,28.8345021 L107.318996,15.4824684 L102.421505,15.4824684 Z M129.168459,34.6072931 L129.168459,7 L124.270968,7.66760168 L124.270968,34.6072931 L129.168459,34.6072931 Z M144.394265,30.601683 C143.551254,30.8373072 142.6681,30.9943899 141.94552,30.9943899 C140.660932,30.9943899 140.179211,30.3267882 140.179211,29.3057504 L140.179211,19.2917251 L144.875986,19.2917251 L145.197133,15.4824684 L140.179211,15.4824684 L140.179211,10.0631136 L135.28172,10.7307153 L135.28172,15.4824684 L132.351254,15.4824684 L132.351254,19.2917251 L135.28172,19.2917251 L135.28172,29.9340813 C135.28172,33.3506311 137.088172,35 140.660932,35 C141.905376,35 143.912545,34.6858345 144.956272,34.2538569 L144.394265,30.601683 Z"></path>
|
||||
</svg>
|
||||
<div class="message is-success">
|
||||
<svg id="checkbox" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 512 512">
|
||||
<path d="M256 32C132.3 32 32 132.3 32 256s100.3 224 224 224 224-100.3 224-224S379.7 32 256 32zm114.9 149.1L231.8 359.6c-1.1 1.1-2.9 3.5-5.1 3.5-2.3 0-3.8-1.6-5.1-2.9-1.3-1.3-78.9-75.9-78.9-75.9l-1.5-1.5c-.6-.9-1.1-2-1.1-3.2 0-1.2.5-2.3 1.1-3.2.4-.4.7-.7 1.1-1.2 7.7-8.1 23.3-24.5 24.3-25.5 1.3-1.3 2.4-3 4.8-3 2.5 0 4.1 2.1 5.3 3.3 1.2 1.2 45 43.3 45 43.3l111.3-143c1-.8 2.2-1.4 3.5-1.4 1.3 0 2.5.5 3.5 1.3l30.6 24.1c.8 1 1.3 2.2 1.3 3.5.1 1.3-.4 2.4-1 3.3z"></path>
|
||||
</svg>
|
||||
<div class="message-content">
|
||||
<div class="message-title">
|
||||
Signed in via your OIDC provider
|
||||
</div>
|
||||
<p class="message-body">
|
||||
You can now close this window and start using Vault.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<h1>Not sure how to get started?</h1>
|
||||
<p class="learn">
|
||||
Check out beginner and advanced guides on HashiCorp Vault at the HashiCorp Learn site or read more in the official documentation.
|
||||
</p>
|
||||
<a href="https://learn.hashicorp.com/vault" rel="noreferrer noopener">
|
||||
<span class="icon">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.338 2.255a.79.79 0 0 0-.645 0L.657 5.378c-.363.162-.534.538-.534.875 0 .337.171.713.534.875l1.436.637c-.332.495-.638 1.18-.744 2.106a.887.887 0 0 0-.26 1.559c.02.081.03.215.013.392-.02.205-.074.43-.162.636-.186.431-.45.64-.741.64v.98c.651 0 1.108-.365 1.403-.797l.06.073c.32.372.826.763 1.455.763v-.98c-.215 0-.474-.145-.71-.42-.111-.13-.2-.27-.259-.393a1.014 1.014 0 0 1-.06-.155c-.01-.036-.013-.055-.013-.058h-.022a2.544 2.544 0 0 0 .031-.641.886.886 0 0 0-.006-1.51c.1-.868.398-1.477.699-1.891l.332.147-.023.746v2.228c0 .115.04.22.105.304.124.276.343.5.587.677.297.217.675.396 1.097.54.846.288 1.943.456 3.127.456 1.185 0 2.281-.168 3.128-.456.422-.144.8-.323 1.097-.54.244-.177.462-.401.586-.677a.488.488 0 0 0 .106-.304V8.218l2.455-1.09c.363-.162.534-.538.534-.875 0-.337-.17-.713-.534-.875L8.338 2.255zm-.34 2.955L3.64 7.38l4.375 1.942 6.912-3.069-6.912-3.07-6.912 3.07 1.665.74 4.901-2.44.328.657zM14.307 1H12.5a.5.5 0 1 1 0-1h3a.499.499 0 0 1 .5.65V3.5a.5.5 0 1 1-1 0V1.72l-1.793 1.774a.5.5 0 0 1-.713-.701L14.307 1zm-2.368 7.653v2.383a.436.436 0 0 0-.007.021c-.017.063-.084.178-.282.322-.193.14-.473.28-.836.404-.724.247-1.71.404-2.812.404-1.1 0-2.087-.157-2.811-.404a3.188 3.188 0 0 1-.836-.404c-.198-.144-.265-.26-.282-.322a.437.437 0 0 0-.007-.02V8.983l.01-.338 3.617 1.605a.791.791 0 0 0 .645 0l3.6-1.598z" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</span>
|
||||
Get started with Vault
|
||||
</a>
|
||||
<a href="https://vaultproject.io/docs" rel="noreferrer noopener">
|
||||
<span class="icon">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.307 1H11.5a.5.5 0 1 1 0-1h3a.499.499 0 0 1 .5.65V3.5a.5.5 0 1 1-1 0V1.72l-1.793 1.774a.5.5 0 0 1-.713-.701L13.307 1zM12 14V8a.5.5 0 1 1 1 0v6.5a.5.5 0 0 1-.5.5H.563a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 .5-.5H8a.5.5 0 0 1 0 1H1v12h11zM4 6a.5.5 0 0 1 0-1h3a.5.5 0 0 1 0 1H4zm0 2.5a.5.5 0 0 1 0-1h5a.5.5 0 0 1 0 1H4zM4 11a.5.5 0 1 1 0-1h5a.5.5 0 1 1 0 1H4z"/>
|
||||
</svg>
|
||||
</span>
|
||||
View the official Vault documentation
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
59
vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_config.go
generated
vendored
59
vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_config.go
generated
vendored
|
|
@ -4,6 +4,7 @@ import (
|
|||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"context"
|
||||
|
|
@ -29,19 +30,44 @@ func pathConfig(b *jwtAuthBackend) *framework.Path {
|
|||
Type: framework.TypeString,
|
||||
Description: "The CA certificate or chain of certificates, in PEM format, to use to validate conections to the OIDC Discovery URL. If not set, system certificates are used.",
|
||||
},
|
||||
"oidc_client_id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "The OAuth Client ID configured with your OIDC provider.",
|
||||
},
|
||||
"oidc_client_secret": {
|
||||
Type: framework.TypeString,
|
||||
Description: "The OAuth Client Secret configured with your OIDC provider.",
|
||||
DisplaySensitive: true,
|
||||
},
|
||||
"default_role": {
|
||||
Type: framework.TypeString,
|
||||
Description: "The default role to use if none is provided during login. If not set, a role is required during login.",
|
||||
},
|
||||
"jwt_validation_pubkeys": {
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: `A list of PEM-encoded public keys to use to authenticate signatures locally. Cannot be used with "oidc_discovery_url".`,
|
||||
},
|
||||
"jwt_supported_algs": {
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: `A list of supported signing algorithms. Defaults to RS256.`,
|
||||
},
|
||||
"bound_issuer": {
|
||||
Type: framework.TypeString,
|
||||
Description: "The value against which to match the 'iss' claim in a JWT. Optional.",
|
||||
},
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.ReadOperation: b.pathConfigRead,
|
||||
logical.UpdateOperation: b.pathConfigWrite,
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.ReadOperation: &framework.PathOperation{
|
||||
Callback: b.pathConfigRead,
|
||||
Summary: "Read the current JWT authentication backend configuration.",
|
||||
},
|
||||
|
||||
logical.UpdateOperation: &framework.PathOperation{
|
||||
Callback: b.pathConfigWrite,
|
||||
Summary: "Configure the JWT authentication backend.",
|
||||
Description: confHelpDesc,
|
||||
},
|
||||
},
|
||||
|
||||
HelpSynopsis: confHelpSyn,
|
||||
|
|
@ -98,7 +124,11 @@ func (b *jwtAuthBackend) pathConfigRead(ctx context.Context, req *logical.Reques
|
|||
Data: map[string]interface{}{
|
||||
"oidc_discovery_url": config.OIDCDiscoveryURL,
|
||||
"oidc_discovery_ca_pem": config.OIDCDiscoveryCAPEM,
|
||||
"oidc_client_id": config.OIDCClientID,
|
||||
"oidc_client_secret": config.OIDCClientSecret,
|
||||
"default_role": config.DefaultRole,
|
||||
"jwt_validation_pubkeys": config.JWTValidationPubKeys,
|
||||
"jwt_supported_algs": config.JWTSupportedAlgs,
|
||||
"bound_issuer": config.BoundIssuer,
|
||||
},
|
||||
}
|
||||
|
|
@ -110,7 +140,11 @@ func (b *jwtAuthBackend) pathConfigWrite(ctx context.Context, req *logical.Reque
|
|||
config := &jwtConfig{
|
||||
OIDCDiscoveryURL: d.Get("oidc_discovery_url").(string),
|
||||
OIDCDiscoveryCAPEM: d.Get("oidc_discovery_ca_pem").(string),
|
||||
OIDCClientID: d.Get("oidc_client_id").(string),
|
||||
OIDCClientSecret: d.Get("oidc_client_secret").(string),
|
||||
DefaultRole: d.Get("default_role").(string),
|
||||
JWTValidationPubKeys: d.Get("jwt_validation_pubkeys").([]string),
|
||||
JWTSupportedAlgs: d.Get("jwt_supported_algs").([]string),
|
||||
BoundIssuer: d.Get("bound_issuer").(string),
|
||||
}
|
||||
|
||||
|
|
@ -120,12 +154,19 @@ func (b *jwtAuthBackend) pathConfigWrite(ctx context.Context, req *logical.Reque
|
|||
config.OIDCDiscoveryURL != "" && len(config.JWTValidationPubKeys) != 0:
|
||||
return logical.ErrorResponse("exactly one of 'oidc_discovery_url' and 'jwt_validation_pubkeys' must be set"), nil
|
||||
|
||||
case config.OIDCClientID != "" && config.OIDCClientSecret == "",
|
||||
config.OIDCClientID == "" && config.OIDCClientSecret != "":
|
||||
return logical.ErrorResponse("both 'oidc_client_id' and 'oidc_client_secret' must be set for OIDC"), nil
|
||||
|
||||
case config.OIDCDiscoveryURL != "":
|
||||
_, err := b.createProvider(config)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(errwrap.Wrapf("error checking discovery URL: {{err}}", err).Error()), nil
|
||||
}
|
||||
|
||||
case config.OIDCClientID != "" && config.OIDCDiscoveryURL == "":
|
||||
return logical.ErrorResponse("'oidc_discovery_url' must be set for OIDC"), nil
|
||||
|
||||
case len(config.JWTValidationPubKeys) != 0:
|
||||
for _, v := range config.JWTValidationPubKeys {
|
||||
if _, err := certutil.ParsePublicKeyPEM([]byte(v)); err != nil {
|
||||
|
|
@ -137,6 +178,14 @@ func (b *jwtAuthBackend) pathConfigWrite(ctx context.Context, req *logical.Reque
|
|||
return nil, errors.New("unknown condition")
|
||||
}
|
||||
|
||||
for _, a := range config.JWTSupportedAlgs {
|
||||
switch a {
|
||||
case oidc.RS256, oidc.RS384, oidc.RS512, oidc.ES256, oidc.ES384, oidc.ES512, oidc.PS256, oidc.PS384, oidc.PS512:
|
||||
default:
|
||||
return logical.ErrorResponse(fmt.Sprintf("Invalid supported algorithm: %s", a)), nil
|
||||
}
|
||||
}
|
||||
|
||||
entry, err := logical.StorageEntryJSON(configPath, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -181,8 +230,12 @@ func (b *jwtAuthBackend) createProvider(config *jwtConfig) (*oidc.Provider, erro
|
|||
type jwtConfig struct {
|
||||
OIDCDiscoveryURL string `json:"oidc_discovery_url"`
|
||||
OIDCDiscoveryCAPEM string `json:"oidc_discovery_ca_pem"`
|
||||
OIDCClientID string `json:"oidc_client_id"`
|
||||
OIDCClientSecret string `json:"oidc_client_secret"`
|
||||
JWTValidationPubKeys []string `json:"jwt_validation_pubkeys"`
|
||||
JWTSupportedAlgs []string `json:"jwt_supported_algs"`
|
||||
BoundIssuer string `json:"bound_issuer"`
|
||||
DefaultRole string `json:"default_role"`
|
||||
|
||||
ParsedJWTPubKeys []interface{} `json:"-"`
|
||||
}
|
||||
|
|
|
|||
263
vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_login.go
generated
vendored
263
vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_login.go
generated
vendored
|
|
@ -29,9 +29,14 @@ func pathLogin(b *jwtAuthBackend) *framework.Path {
|
|||
},
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.UpdateOperation: b.pathLogin,
|
||||
logical.AliasLookaheadOperation: b.pathLogin,
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.UpdateOperation: &framework.PathOperation{
|
||||
Callback: b.pathLogin,
|
||||
Summary: pathLoginHelpSyn,
|
||||
},
|
||||
logical.AliasLookaheadOperation: &framework.PathOperation{
|
||||
Callback: b.pathLogin,
|
||||
},
|
||||
},
|
||||
|
||||
HelpSynopsis: pathLoginHelpSyn,
|
||||
|
|
@ -40,13 +45,19 @@ func pathLogin(b *jwtAuthBackend) *framework.Path {
|
|||
}
|
||||
|
||||
func (b *jwtAuthBackend) pathLogin(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
token := d.Get("jwt").(string)
|
||||
if len(token) == 0 {
|
||||
return logical.ErrorResponse("missing token"), nil
|
||||
config, err := b.config(ctx, req.Storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if config == nil {
|
||||
return logical.ErrorResponse("could not load configuration"), nil
|
||||
}
|
||||
|
||||
roleName := d.Get("role").(string)
|
||||
if len(roleName) == 0 {
|
||||
if roleName == "" {
|
||||
roleName = config.DefaultRole
|
||||
}
|
||||
if roleName == "" {
|
||||
return logical.ErrorResponse("missing role"), nil
|
||||
}
|
||||
|
||||
|
|
@ -55,21 +66,18 @@ func (b *jwtAuthBackend) pathLogin(ctx context.Context, req *logical.Request, d
|
|||
return nil, err
|
||||
}
|
||||
if role == nil {
|
||||
return logical.ErrorResponse("role could not be found"), nil
|
||||
return logical.ErrorResponse("role %q could not be found", roleName), nil
|
||||
}
|
||||
|
||||
token := d.Get("jwt").(string)
|
||||
if len(token) == 0 {
|
||||
return logical.ErrorResponse("missing token"), nil
|
||||
}
|
||||
|
||||
if req.Connection != nil && !cidrutil.RemoteAddrIsOk(req.Connection.RemoteAddr, role.BoundCIDRs) {
|
||||
return logical.ErrorResponse("request originated from invalid CIDR"), nil
|
||||
}
|
||||
|
||||
config, err := b.config(ctx, req.Storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if config == nil {
|
||||
return logical.ErrorResponse("could not load configuration"), nil
|
||||
}
|
||||
|
||||
// Here is where things diverge. If it is using OIDC Discovery, validate
|
||||
// that way; otherwise validate against the locally configured keys. Once
|
||||
// things are validated, we re-unify the request path when evaluating the
|
||||
|
|
@ -130,118 +138,37 @@ func (b *jwtAuthBackend) pathLogin(ctx context.Context, req *logical.Request, d
|
|||
}
|
||||
|
||||
case config.OIDCDiscoveryURL != "":
|
||||
provider, err := b.getProvider(ctx, config)
|
||||
allClaims, err = b.verifyToken(ctx, config, role, token)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("error getting provider for login operation: {{err}}", err)
|
||||
}
|
||||
|
||||
verifier := provider.Verifier(&oidc.Config{
|
||||
SkipClientIDCheck: true,
|
||||
})
|
||||
|
||||
idToken, err := verifier.Verify(ctx, token)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(errwrap.Wrapf("error validating signature: {{err}}", err).Error()), nil
|
||||
}
|
||||
|
||||
if err := idToken.Claims(&allClaims); err != nil {
|
||||
return logical.ErrorResponse(errwrap.Wrapf("unable to successfully parse all claims from token: {{err}}", err).Error()), nil
|
||||
}
|
||||
|
||||
if role.BoundSubject != "" && role.BoundSubject != idToken.Subject {
|
||||
return logical.ErrorResponse("sub claim does not match bound subject"), nil
|
||||
}
|
||||
if len(role.BoundAudiences) != 0 {
|
||||
var found bool
|
||||
for _, v := range role.BoundAudiences {
|
||||
if strutil.StrListContains(idToken.Audience, v) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return logical.ErrorResponse("aud claim does not match any bound audience"), nil
|
||||
}
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, errors.New("unhandled case during login")
|
||||
}
|
||||
|
||||
userClaimRaw, ok := allClaims[role.UserClaim]
|
||||
if !ok {
|
||||
return logical.ErrorResponse(fmt.Sprintf("%q claim not found in token", role.UserClaim)), nil
|
||||
}
|
||||
userName, ok := userClaimRaw.(string)
|
||||
if !ok {
|
||||
return logical.ErrorResponse(fmt.Sprintf("%q claim could not be converted to string", role.UserClaim)), nil
|
||||
alias, groupAliases, err := b.createIdentity(allClaims, role)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
}
|
||||
|
||||
var groupAliases []*logical.Alias
|
||||
if role.GroupsClaim != "" {
|
||||
mapPath, err := parseClaimWithDelimiters(role.GroupsClaim, role.GroupsClaimDelimiterPattern)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(errwrap.Wrapf("error parsing delimiters for groups claim: {{err}}", err).Error()), nil
|
||||
}
|
||||
if len(mapPath) < 1 {
|
||||
return logical.ErrorResponse("unexpected length 0 of claims path after parsing groups claim against delimiters"), nil
|
||||
}
|
||||
var claimKey string
|
||||
claimMap := allClaims
|
||||
for i, key := range mapPath {
|
||||
if i == len(mapPath)-1 {
|
||||
claimKey = key
|
||||
break
|
||||
}
|
||||
nextMapRaw, ok := claimMap[key]
|
||||
if !ok {
|
||||
return logical.ErrorResponse(fmt.Sprintf("map via key %q not found while navigating group claim delimiters", key)), nil
|
||||
}
|
||||
nextMap, ok := nextMapRaw.(map[string]interface{})
|
||||
if !ok {
|
||||
return logical.ErrorResponse(fmt.Sprintf("key %q does not reference a map while navigating group claim delimiters", key)), nil
|
||||
}
|
||||
claimMap = nextMap
|
||||
}
|
||||
|
||||
groupsClaimRaw, ok := claimMap[claimKey]
|
||||
if !ok {
|
||||
return logical.ErrorResponse(fmt.Sprintf("%q claim not found in token", role.GroupsClaim)), nil
|
||||
}
|
||||
groups, ok := groupsClaimRaw.([]interface{})
|
||||
if !ok {
|
||||
return logical.ErrorResponse(fmt.Sprintf("%q claim could not be converted to string list", role.GroupsClaim)), nil
|
||||
}
|
||||
for _, groupRaw := range groups {
|
||||
group, ok := groupRaw.(string)
|
||||
if !ok {
|
||||
return logical.ErrorResponse(fmt.Sprintf("value %v in groups claim could not be parsed as string", groupRaw)), nil
|
||||
}
|
||||
if group == "" {
|
||||
continue
|
||||
}
|
||||
groupAliases = append(groupAliases, &logical.Alias{
|
||||
Name: group,
|
||||
})
|
||||
}
|
||||
tokenMetadata := map[string]string{"role": roleName}
|
||||
for k, v := range alias.Metadata {
|
||||
tokenMetadata[k] = v
|
||||
}
|
||||
|
||||
resp := &logical.Response{
|
||||
Auth: &logical.Auth{
|
||||
Policies: role.Policies,
|
||||
DisplayName: userName,
|
||||
Period: role.Period,
|
||||
NumUses: role.NumUses,
|
||||
Alias: &logical.Alias{
|
||||
Name: userName,
|
||||
},
|
||||
Policies: role.Policies,
|
||||
DisplayName: alias.Name,
|
||||
Period: role.Period,
|
||||
NumUses: role.NumUses,
|
||||
Alias: alias,
|
||||
GroupAliases: groupAliases,
|
||||
InternalData: map[string]interface{}{
|
||||
"role": roleName,
|
||||
},
|
||||
Metadata: map[string]string{
|
||||
"role": roleName,
|
||||
},
|
||||
Metadata: tokenMetadata,
|
||||
LeaseOptions: logical.LeaseOptions{
|
||||
Renewable: true,
|
||||
TTL: role.TTL,
|
||||
|
|
@ -276,6 +203,120 @@ func (b *jwtAuthBackend) pathLoginRenew(ctx context.Context, req *logical.Reques
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
func (b *jwtAuthBackend) verifyToken(ctx context.Context, config *jwtConfig, role *jwtRole, rawToken string) (map[string]interface{}, error) {
|
||||
allClaims := make(map[string]interface{})
|
||||
|
||||
provider, err := b.getProvider(ctx, config)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("error getting provider for login operation: {{err}}", err)
|
||||
}
|
||||
|
||||
oidcConfig := &oidc.Config{
|
||||
SupportedSigningAlgs: config.JWTSupportedAlgs,
|
||||
}
|
||||
|
||||
if role.RoleType == "oidc" {
|
||||
oidcConfig.ClientID = config.OIDCClientID
|
||||
} else {
|
||||
oidcConfig.SkipClientIDCheck = true
|
||||
}
|
||||
verifier := provider.Verifier(oidcConfig)
|
||||
|
||||
idToken, err := verifier.Verify(ctx, rawToken)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("error validating signature: {{err}}", err)
|
||||
}
|
||||
|
||||
if err := idToken.Claims(&allClaims); err != nil {
|
||||
return nil, errwrap.Wrapf("unable to successfully parse all claims from token: {{err}}", err)
|
||||
}
|
||||
|
||||
if role.BoundSubject != "" && role.BoundSubject != idToken.Subject {
|
||||
return nil, errors.New("sub claim does not match bound subject")
|
||||
}
|
||||
if len(role.BoundAudiences) > 0 {
|
||||
var found bool
|
||||
for _, v := range role.BoundAudiences {
|
||||
if strutil.StrListContains(idToken.Audience, v) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, errors.New("aud claim does not match any bound audience")
|
||||
}
|
||||
}
|
||||
|
||||
if len(role.BoundClaims) > 0 {
|
||||
for claim, expValue := range role.BoundClaims {
|
||||
actValue := getClaim(b.Logger(), allClaims, claim)
|
||||
if actValue == nil {
|
||||
return nil, fmt.Errorf("claim is missing: %s", claim)
|
||||
}
|
||||
|
||||
if expValue != actValue {
|
||||
return nil, fmt.Errorf("claim '%s' does not match associated bound claim", claim)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allClaims, nil
|
||||
}
|
||||
|
||||
// createIdentity creates an alias and set of groups aliass based on the role
|
||||
// definition and received claims.
|
||||
func (b *jwtAuthBackend) createIdentity(allClaims map[string]interface{}, role *jwtRole) (*logical.Alias, []*logical.Alias, error) {
|
||||
userClaimRaw, ok := allClaims[role.UserClaim]
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("claim %q not found in token", role.UserClaim)
|
||||
}
|
||||
userName, ok := userClaimRaw.(string)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("claim %q could not be converted to string", role.UserClaim)
|
||||
}
|
||||
|
||||
metadata, err := extractMetadata(b.Logger(), allClaims, role.ClaimMappings)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
alias := &logical.Alias{
|
||||
Name: userName,
|
||||
Metadata: metadata,
|
||||
}
|
||||
|
||||
var groupAliases []*logical.Alias
|
||||
|
||||
if role.GroupsClaim == "" {
|
||||
return alias, groupAliases, nil
|
||||
}
|
||||
|
||||
groupsClaimRaw := getClaim(b.Logger(), allClaims, role.GroupsClaim)
|
||||
|
||||
if groupsClaimRaw == nil {
|
||||
return nil, nil, fmt.Errorf("%q claim not found in token", role.GroupsClaim)
|
||||
}
|
||||
groups, ok := groupsClaimRaw.([]interface{})
|
||||
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("%q claim could not be converted to string list", role.GroupsClaim)
|
||||
}
|
||||
for _, groupRaw := range groups {
|
||||
group, ok := groupRaw.(string)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("value %v in groups claim could not be parsed as string", groupRaw)
|
||||
}
|
||||
if group == "" {
|
||||
continue
|
||||
}
|
||||
groupAliases = append(groupAliases, &logical.Alias{
|
||||
Name: group,
|
||||
})
|
||||
}
|
||||
|
||||
return alias, groupAliases, nil
|
||||
}
|
||||
|
||||
const (
|
||||
pathLoginHelpSyn = `
|
||||
Authenticates to Vault using a JWT (or OIDC) token.
|
||||
|
|
|
|||
303
vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_oidc.go
generated
vendored
Normal file
303
vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_oidc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
package jwtauth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
oidc "github.com/coreos/go-oidc"
|
||||
"github.com/hashicorp/errwrap"
|
||||
uuid "github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/vault/helper/strutil"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
var oidcStateTimeout = 10 * time.Minute
|
||||
|
||||
// OIDC error prefixes. This are searched for specifically by the UI, so any
|
||||
// changes to them must be aligned with a UI change.
|
||||
const errLoginFailed = "Vault login failed."
|
||||
const errNoResponse = "No response from provider."
|
||||
const errTokenVerification = "Token verification failed."
|
||||
|
||||
// oidcState is created when an authURL is requested. The state identifier is
|
||||
// passed throughout the OAuth process.
|
||||
type oidcState struct {
|
||||
rolename string
|
||||
nonce string
|
||||
redirectURI string
|
||||
}
|
||||
|
||||
func pathOIDC(b *jwtAuthBackend) []*framework.Path {
|
||||
return []*framework.Path{
|
||||
{
|
||||
Pattern: `oidc/callback`,
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"state": {
|
||||
Type: framework.TypeString,
|
||||
},
|
||||
"code": {
|
||||
Type: framework.TypeString,
|
||||
},
|
||||
},
|
||||
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.ReadOperation: &framework.PathOperation{
|
||||
Callback: b.pathCallback,
|
||||
Summary: "Callback endpoint to complete an OIDC login.",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Pattern: `oidc/auth_url`,
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"role": {
|
||||
Type: framework.TypeLowerCaseString,
|
||||
Description: "The role to issue an OIDC authorization URL against.",
|
||||
},
|
||||
"redirect_uri": {
|
||||
Type: framework.TypeString,
|
||||
Description: "The OAuth redirect_uri to use in the authorization URL.",
|
||||
},
|
||||
},
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.UpdateOperation: &framework.PathOperation{
|
||||
Callback: b.authURL,
|
||||
Summary: "Request an authorization URL to start an OIDC login flow.",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (b *jwtAuthBackend) pathCallback(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
state := b.verifyState(d.Get("state").(string))
|
||||
if state == nil {
|
||||
return logical.ErrorResponse(errLoginFailed + " Expired or missing OAuth state."), nil
|
||||
}
|
||||
|
||||
roleName := state.rolename
|
||||
role, err := b.role(ctx, req.Storage, roleName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if role == nil {
|
||||
return logical.ErrorResponse(errLoginFailed + " Role could not be found"), nil
|
||||
}
|
||||
|
||||
config, err := b.config(ctx, req.Storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if config == nil {
|
||||
return logical.ErrorResponse(errLoginFailed + " Could not load configuration"), nil
|
||||
}
|
||||
|
||||
provider, err := b.getProvider(ctx, config)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf(errLoginFailed+" Error getting provider for login operation: {{err}}", err)
|
||||
}
|
||||
|
||||
var oauth2Config = oauth2.Config{
|
||||
ClientID: config.OIDCClientID,
|
||||
ClientSecret: config.OIDCClientSecret,
|
||||
RedirectURL: state.redirectURI,
|
||||
Endpoint: provider.Endpoint(),
|
||||
Scopes: []string{oidc.ScopeOpenID},
|
||||
}
|
||||
|
||||
code := d.Get("code").(string)
|
||||
if code == "" {
|
||||
return logical.ErrorResponse(errLoginFailed + " OAuth code parameter not provided"), nil
|
||||
}
|
||||
|
||||
oauth2Token, err := oauth2Config.Exchange(ctx, code)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(errLoginFailed+" Error exchanging oidc code: %q.", err.Error()), nil
|
||||
}
|
||||
|
||||
// Extract the ID Token from OAuth2 token.
|
||||
rawToken, ok := oauth2Token.Extra("id_token").(string)
|
||||
if !ok {
|
||||
return logical.ErrorResponse(errTokenVerification + " No id_token found in response."), nil
|
||||
}
|
||||
|
||||
// Parse and verify ID Token payload.
|
||||
allClaims, err := b.verifyToken(ctx, config, role, rawToken)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse("%s %s", errTokenVerification, err.Error()), nil
|
||||
}
|
||||
|
||||
// Attempt to fetch information from the /userinfo endpoint and merge it with
|
||||
// the existing claims data. A failure to fetch additional information from this
|
||||
// endpoint will not invalidate the authorization flow.
|
||||
if userinfo, err := provider.UserInfo(ctx, oauth2.StaticTokenSource(oauth2Token)); err == nil {
|
||||
_ = userinfo.Claims(&allClaims)
|
||||
} else {
|
||||
logFunc := b.Logger().Warn
|
||||
if strings.Contains(err.Error(), "user info endpoint is not supported") {
|
||||
logFunc = b.Logger().Info
|
||||
}
|
||||
logFunc("error reading /userinfo endpoint", "error", err)
|
||||
}
|
||||
|
||||
if allClaims["nonce"] != state.nonce {
|
||||
return logical.ErrorResponse(errTokenVerification + " Invalid ID token nonce."), nil
|
||||
}
|
||||
delete(allClaims, "nonce")
|
||||
|
||||
alias, groupAliases, err := b.createIdentity(allClaims, role)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
}
|
||||
|
||||
tokenMetadata := map[string]string{"role": roleName}
|
||||
for k, v := range alias.Metadata {
|
||||
tokenMetadata[k] = v
|
||||
}
|
||||
|
||||
resp := &logical.Response{
|
||||
Auth: &logical.Auth{
|
||||
Policies: role.Policies,
|
||||
DisplayName: alias.Name,
|
||||
Period: role.Period,
|
||||
NumUses: role.NumUses,
|
||||
Alias: alias,
|
||||
GroupAliases: groupAliases,
|
||||
InternalData: map[string]interface{}{
|
||||
"role": roleName,
|
||||
},
|
||||
Metadata: tokenMetadata,
|
||||
LeaseOptions: logical.LeaseOptions{
|
||||
Renewable: true,
|
||||
TTL: role.TTL,
|
||||
MaxTTL: role.MaxTTL,
|
||||
},
|
||||
BoundCIDRs: role.BoundCIDRs,
|
||||
},
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// authURL returns a URL used for redirection to receive an authorization code.
|
||||
// This path requires a role name, or that a default_role has been configured.
|
||||
// Because this endpoint is unauthenticated, the response to invalid or non-OIDC
|
||||
// roles is intentionally non-descriptive and will simply be an empty string.
|
||||
func (b *jwtAuthBackend) authURL(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
logger := b.Logger()
|
||||
|
||||
// default response for most error/invalid conditions
|
||||
resp := &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"auth_url": "",
|
||||
},
|
||||
}
|
||||
|
||||
config, err := b.config(ctx, req.Storage)
|
||||
if err != nil {
|
||||
logger.Warn("error loading configuration", "error", err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
logger.Warn("nil configuration")
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
roleName := d.Get("role").(string)
|
||||
if roleName == "" {
|
||||
roleName = config.DefaultRole
|
||||
if roleName == "" {
|
||||
return logical.ErrorResponse("missing role"), nil
|
||||
}
|
||||
}
|
||||
|
||||
redirectURI := d.Get("redirect_uri").(string)
|
||||
if redirectURI == "" {
|
||||
return logical.ErrorResponse("missing redirect_uri"), nil
|
||||
}
|
||||
|
||||
role, err := b.role(ctx, req.Storage, roleName)
|
||||
if err != nil {
|
||||
logger.Warn("error loading role", "error", err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
if role == nil || role.RoleType != "oidc" {
|
||||
logger.Warn("invalid role type", "role type", role)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
if !strutil.StrListContains(role.AllowedRedirectURIs, redirectURI) {
|
||||
logger.Warn("unauthorized redirect_uri", "redirect_uri", redirectURI)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
provider, err := b.getProvider(ctx, config)
|
||||
if err != nil {
|
||||
logger.Warn("error getting provider for login operation", "error", err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// "openid" is a required scope for OpenID Connect flows
|
||||
scopes := append([]string{oidc.ScopeOpenID}, role.OIDCScopes...)
|
||||
|
||||
// Configure an OpenID Connect aware OAuth2 client
|
||||
oauth2Config := oauth2.Config{
|
||||
ClientID: config.OIDCClientID,
|
||||
ClientSecret: config.OIDCClientSecret,
|
||||
RedirectURL: redirectURI,
|
||||
Endpoint: provider.Endpoint(),
|
||||
Scopes: scopes,
|
||||
}
|
||||
|
||||
stateID, nonce, err := b.createState(roleName, redirectURI)
|
||||
if err != nil {
|
||||
logger.Warn("error generating OAuth state", "error", err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
resp.Data["auth_url"] = oauth2Config.AuthCodeURL(stateID, oidc.Nonce(nonce))
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// createState make an expiring state object, associated with a random state ID
|
||||
// that is passed throughout the OAuth process. A nonce is also included in the
|
||||
// auth process, and for simplicity will be identical in length/format as the state ID.
|
||||
func (b *jwtAuthBackend) createState(rolename, redirectURI string) (string, string, error) {
|
||||
// Get enough bytes for 2 160-bit IDs (per rfc6749#section-10.10)
|
||||
bytes, err := uuid.GenerateRandomBytes(2 * 20)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
stateID := fmt.Sprintf("%x", bytes[:20])
|
||||
nonce := fmt.Sprintf("%x", bytes[20:])
|
||||
|
||||
b.oidcStates.SetDefault(stateID, &oidcState{
|
||||
rolename: rolename,
|
||||
nonce: nonce,
|
||||
redirectURI: redirectURI,
|
||||
})
|
||||
|
||||
return stateID, nonce, nil
|
||||
}
|
||||
|
||||
// verifyState tests whether the provided state ID is valid and returns the
|
||||
// associated state object if so. A nil state is returned if the ID is not found
|
||||
// or expired. The state should only ever be retrieved once and is deleted as
|
||||
// part of this request.
|
||||
func (b *jwtAuthBackend) verifyState(stateID string) *oidcState {
|
||||
defer b.oidcStates.Delete(stateID)
|
||||
|
||||
if stateRaw, ok := b.oidcStates.Get(stateID); ok {
|
||||
return stateRaw.(*oidcState)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
190
vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_role.go
generated
vendored
190
vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_role.go
generated
vendored
|
|
@ -7,19 +7,25 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
sockaddr "github.com/hashicorp/go-sockaddr"
|
||||
"github.com/hashicorp/vault/helper/parseutil"
|
||||
"github.com/hashicorp/vault/helper/policyutil"
|
||||
"github.com/hashicorp/vault/helper/strutil"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
|
||||
var reservedMetadata = []string{"role"}
|
||||
|
||||
func pathRoleList(b *jwtAuthBackend) *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: "role/?",
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.ListOperation: b.pathRoleList,
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.ListOperation: &framework.PathOperation{
|
||||
Callback: b.pathRoleList,
|
||||
Summary: strings.TrimSpace(roleHelp["role-list"][0]),
|
||||
Description: strings.TrimSpace(roleHelp["role-list"][1]),
|
||||
},
|
||||
},
|
||||
HelpSynopsis: strings.TrimSpace(roleHelp["role-list"][0]),
|
||||
HelpDescription: strings.TrimSpace(roleHelp["role-list"][1]),
|
||||
|
|
@ -35,6 +41,10 @@ func pathRole(b *jwtAuthBackend) *framework.Path {
|
|||
Type: framework.TypeLowerCaseString,
|
||||
Description: "Name of the role.",
|
||||
},
|
||||
"role_type": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Type of the role, either 'jwt' or 'oidc'.",
|
||||
},
|
||||
"policies": {
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: "List of policies on the role.",
|
||||
|
|
@ -68,6 +78,14 @@ TTL will be set to the value of this parameter.`,
|
|||
Type: framework.TypeCommaStringSlice,
|
||||
Description: `Comma-separated list of 'aud' claims that are valid for login; any match is sufficient`,
|
||||
},
|
||||
"bound_claims": {
|
||||
Type: framework.TypeMap,
|
||||
Description: `Map of claims/values which must match for login`,
|
||||
},
|
||||
"claim_mappings": {
|
||||
Type: framework.TypeKVPairs,
|
||||
Description: `Mappings of claims (key) that will be copied to a metadata field (value)`,
|
||||
},
|
||||
"user_claim": {
|
||||
Type: framework.TypeString,
|
||||
Description: `The claim to use for the Identity entity alias name`,
|
||||
|
|
@ -76,22 +94,43 @@ TTL will be set to the value of this parameter.`,
|
|||
Type: framework.TypeString,
|
||||
Description: `The claim to use for the Identity group alias names`,
|
||||
},
|
||||
"groups_claim_delimiter_pattern": {
|
||||
Type: framework.TypeString,
|
||||
Description: `A pattern of delimiters used to allow the groups_claim to live outside of the top-level JWT structure. For instance, a "groups_claim" of "meta/user.name/groups" with this field set to "//" will expect nested structures named "meta", "user.name", and "groups". If this field was set to "/./" the groups information would expect to be via nested structures of "meta", "user", "name", and "groups".`,
|
||||
},
|
||||
"bound_cidrs": {
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: `Comma-separated list of IP CIDRS that are allowed to
|
||||
authenticate against this role`,
|
||||
},
|
||||
"oidc_scopes": {
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: `Comma-separated list of OIDC scopes`,
|
||||
},
|
||||
"allowed_redirect_uris": {
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: `Comma-separated list of allowed values for redirect_uri`,
|
||||
},
|
||||
},
|
||||
ExistenceCheck: b.pathRoleExistenceCheck,
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.CreateOperation: b.pathRoleCreateUpdate,
|
||||
logical.UpdateOperation: b.pathRoleCreateUpdate,
|
||||
logical.ReadOperation: b.pathRoleRead,
|
||||
logical.DeleteOperation: b.pathRoleDelete,
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.ReadOperation: &framework.PathOperation{
|
||||
Callback: b.pathRoleRead,
|
||||
Summary: "Read an existing role.",
|
||||
},
|
||||
|
||||
logical.UpdateOperation: &framework.PathOperation{
|
||||
Callback: b.pathRoleCreateUpdate,
|
||||
Summary: strings.TrimSpace(roleHelp["role"][0]),
|
||||
Description: strings.TrimSpace(roleHelp["role"][1]),
|
||||
},
|
||||
|
||||
logical.CreateOperation: &framework.PathOperation{
|
||||
Callback: b.pathRoleCreateUpdate,
|
||||
Summary: strings.TrimSpace(roleHelp["role"][0]),
|
||||
Description: strings.TrimSpace(roleHelp["role"][1]),
|
||||
},
|
||||
|
||||
logical.DeleteOperation: &framework.PathOperation{
|
||||
Callback: b.pathRoleDelete,
|
||||
Summary: "Delete an existing role.",
|
||||
},
|
||||
},
|
||||
HelpSynopsis: strings.TrimSpace(roleHelp["role"][0]),
|
||||
HelpDescription: strings.TrimSpace(roleHelp["role"][1]),
|
||||
|
|
@ -99,6 +138,8 @@ authenticate against this role`,
|
|||
}
|
||||
|
||||
type jwtRole struct {
|
||||
RoleType string `json:"role_type"`
|
||||
|
||||
// Policies that are to be required by the token to access this role
|
||||
Policies []string `json:"policies"`
|
||||
|
||||
|
|
@ -119,12 +160,15 @@ type jwtRole struct {
|
|||
Period time.Duration `json:"period"`
|
||||
|
||||
// Role binding properties
|
||||
BoundAudiences []string `json:"bound_audiences"`
|
||||
BoundSubject string `json:"bound_subject"`
|
||||
BoundCIDRs []*sockaddr.SockAddrMarshaler `json:"bound_cidrs"`
|
||||
UserClaim string `json:"user_claim"`
|
||||
GroupsClaim string `json:"groups_claim"`
|
||||
GroupsClaimDelimiterPattern string `json:"groups_claim_delimiter_pattern"`
|
||||
BoundAudiences []string `json:"bound_audiences"`
|
||||
BoundSubject string `json:"bound_subject"`
|
||||
BoundClaims map[string]interface{} `json:"bound_claims"`
|
||||
ClaimMappings map[string]string `json:"claim_mappings"`
|
||||
BoundCIDRs []*sockaddr.SockAddrMarshaler `json:"bound_cidrs"`
|
||||
UserClaim string `json:"user_claim"`
|
||||
GroupsClaim string `json:"groups_claim"`
|
||||
OIDCScopes []string `json:"oidc_scopes"`
|
||||
AllowedRedirectURIs []string `json:"allowed_redirect_uris"`
|
||||
}
|
||||
|
||||
// role takes a storage backend and the name and returns the role's storage
|
||||
|
|
@ -182,17 +226,20 @@ func (b *jwtAuthBackend) pathRoleRead(ctx context.Context, req *logical.Request,
|
|||
// Create a map of data to be returned
|
||||
resp := &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"policies": role.Policies,
|
||||
"num_uses": role.NumUses,
|
||||
"period": int64(role.Period.Seconds()),
|
||||
"ttl": int64(role.TTL.Seconds()),
|
||||
"max_ttl": int64(role.MaxTTL.Seconds()),
|
||||
"bound_audiences": role.BoundAudiences,
|
||||
"bound_subject": role.BoundSubject,
|
||||
"bound_cidrs": role.BoundCIDRs,
|
||||
"user_claim": role.UserClaim,
|
||||
"groups_claim": role.GroupsClaim,
|
||||
"groups_claim_delimiter_pattern": role.GroupsClaimDelimiterPattern,
|
||||
"role_type": role.RoleType,
|
||||
"policies": role.Policies,
|
||||
"num_uses": role.NumUses,
|
||||
"period": int64(role.Period.Seconds()),
|
||||
"ttl": int64(role.TTL.Seconds()),
|
||||
"max_ttl": int64(role.MaxTTL.Seconds()),
|
||||
"bound_audiences": role.BoundAudiences,
|
||||
"bound_subject": role.BoundSubject,
|
||||
"bound_cidrs": role.BoundCIDRs,
|
||||
"bound_claims": role.BoundClaims,
|
||||
"claim_mappings": role.ClaimMappings,
|
||||
"user_claim": role.UserClaim,
|
||||
"groups_claim": role.GroupsClaim,
|
||||
"allowed_redirect_uris": role.AllowedRedirectURIs,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -236,6 +283,15 @@ func (b *jwtAuthBackend) pathRoleCreateUpdate(ctx context.Context, req *logical.
|
|||
role = new(jwtRole)
|
||||
}
|
||||
|
||||
roleType := data.Get("role_type").(string)
|
||||
if roleType == "" {
|
||||
roleType = "jwt"
|
||||
}
|
||||
if roleType != "jwt" && roleType != "oidc" {
|
||||
return logical.ErrorResponse("invalid 'role_type': %s", roleType), nil
|
||||
}
|
||||
role.RoleType = roleType
|
||||
|
||||
if policiesRaw, ok := data.GetOk("policies"); ok {
|
||||
role.Policies = policyutil.ParsePolicies(policiesRaw)
|
||||
}
|
||||
|
|
@ -287,6 +343,29 @@ func (b *jwtAuthBackend) pathRoleCreateUpdate(ctx context.Context, req *logical.
|
|||
role.BoundCIDRs = parsedCIDRs
|
||||
}
|
||||
|
||||
if boundClaimsRaw, ok := data.GetOk("bound_claims"); ok {
|
||||
role.BoundClaims = boundClaimsRaw.(map[string]interface{})
|
||||
}
|
||||
|
||||
if claimMappingsRaw, ok := data.GetOk("claim_mappings"); ok {
|
||||
claimMappings := claimMappingsRaw.(map[string]string)
|
||||
|
||||
// sanity check mappings for duplicates and collision with reserved names
|
||||
targets := make(map[string]bool)
|
||||
for _, metadataKey := range claimMappings {
|
||||
if strutil.StrListContains(reservedMetadata, metadataKey) {
|
||||
return logical.ErrorResponse("metadata key '%s' is reserved and may not be a mapping destination", metadataKey), nil
|
||||
}
|
||||
|
||||
if targets[metadataKey] {
|
||||
return logical.ErrorResponse("multiple keys are mapped to metadata key '%s'", metadataKey), nil
|
||||
}
|
||||
targets[metadataKey] = true
|
||||
}
|
||||
|
||||
role.ClaimMappings = claimMappings
|
||||
}
|
||||
|
||||
if userClaim, ok := data.GetOk("user_claim"); ok {
|
||||
role.UserClaim = userClaim.(string)
|
||||
}
|
||||
|
|
@ -298,23 +377,26 @@ func (b *jwtAuthBackend) pathRoleCreateUpdate(ctx context.Context, req *logical.
|
|||
role.GroupsClaim = groupsClaim.(string)
|
||||
}
|
||||
|
||||
if groupsClaimDelimiterPattern, ok := data.GetOk("groups_claim_delimiter_pattern"); ok {
|
||||
role.GroupsClaimDelimiterPattern = groupsClaimDelimiterPattern.(string)
|
||||
if oidcScopes, ok := data.GetOk("oidc_scopes"); ok {
|
||||
role.OIDCScopes = oidcScopes.([]string)
|
||||
}
|
||||
|
||||
// Validate claim/delims
|
||||
if role.GroupsClaim != "" {
|
||||
if _, err := parseClaimWithDelimiters(role.GroupsClaim, role.GroupsClaimDelimiterPattern); err != nil {
|
||||
return logical.ErrorResponse(errwrap.Wrapf("error validating delimiters for groups claim: {{err}}", err).Error()), nil
|
||||
allowedRedirectURIs := data.Get("allowed_redirect_uris").([]string)
|
||||
if roleType == "oidc" && len(allowedRedirectURIs) == 0 {
|
||||
return logical.ErrorResponse("'allowed_redirect_uris' must be set"), nil
|
||||
}
|
||||
role.AllowedRedirectURIs = allowedRedirectURIs
|
||||
|
||||
// OIDC verifcation will enforce that the audience match the configured client_id.
|
||||
// For other methods, require at least one bound constraint.
|
||||
if roleType != "oidc" {
|
||||
if len(role.BoundAudiences) == 0 &&
|
||||
len(role.BoundCIDRs) == 0 &&
|
||||
role.BoundSubject == "" {
|
||||
return logical.ErrorResponse("must have at least one bound constraint when creating/updating a role"), nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(role.BoundAudiences) == 0 &&
|
||||
len(role.BoundCIDRs) == 0 &&
|
||||
role.BoundSubject == "" {
|
||||
return logical.ErrorResponse("must have at least one bound constraint when creating/updating a role"), nil
|
||||
}
|
||||
|
||||
// Check that the TTL value provided is less than the MaxTTL.
|
||||
// Sanitizing the TTL and MaxTTL is not required now and can be performed
|
||||
// at credential issue time.
|
||||
|
|
@ -340,32 +422,6 @@ func (b *jwtAuthBackend) pathRoleCreateUpdate(ctx context.Context, req *logical.
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
// parseClaimWithDelimiters parses a given claim string and ensures that we can
|
||||
// separate it out into a "map path"
|
||||
func parseClaimWithDelimiters(claim, delimiters string) ([]string, error) {
|
||||
if delimiters == "" {
|
||||
return []string{claim}, nil
|
||||
}
|
||||
var ret []string
|
||||
for _, runeVal := range delimiters {
|
||||
idx := strings.IndexRune(claim, runeVal)
|
||||
switch idx {
|
||||
case -1:
|
||||
return nil, fmt.Errorf("could not find instance of %q delimiter in claim", string(runeVal))
|
||||
case 0:
|
||||
return nil, fmt.Errorf("instance of %q delimiter in claim is at beginning of claim string", string(runeVal))
|
||||
case len(claim) - 1:
|
||||
return nil, fmt.Errorf("instance of %q delimiter in claim is at end of claim string", string(runeVal))
|
||||
default:
|
||||
ret = append(ret, claim[:idx])
|
||||
claim = claim[idx+1:]
|
||||
}
|
||||
}
|
||||
ret = append(ret, claim)
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// roleStorageEntry stores all the options that are set on an role
|
||||
var roleHelp = map[string][2]string{
|
||||
"role-list": {
|
||||
|
|
|
|||
37
vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_ui.go
generated
vendored
Normal file
37
vendor/github.com/hashicorp/vault-plugin-auth-jwt/path_ui.go
generated
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// A throwaway file for super simple testing via a UI
|
||||
package jwtauth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
|
||||
func pathUI(b *jwtAuthBackend) *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: `ui$`,
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.ReadOperation: b.pathUI,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (b *jwtAuthBackend) pathUI(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
data, err := ioutil.ReadFile("test_ui.html")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
resp := &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
logical.HTTPStatusCode: 200,
|
||||
logical.HTTPRawBody: string(data),
|
||||
logical.HTTPContentType: "text/html",
|
||||
},
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
37
vendor/github.com/hashicorp/vault-plugin-auth-jwt/test_ui.html
generated
vendored
Normal file
37
vendor/github.com/hashicorp/vault-plugin-auth-jwt/test_ui.html
generated
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<!-- A throwaway file for super simple testing via a UI -->
|
||||
<html>
|
||||
<body>
|
||||
Role:<br>
|
||||
<input id="role" type="text" value="test"/><br>
|
||||
<button id="login">Login</button>
|
||||
|
||||
<script>
|
||||
document.getElementById("login").addEventListener("click", doLogin);
|
||||
|
||||
function doLogin() {
|
||||
role = document.getElementById("role").value;
|
||||
|
||||
fetch(`${window.location.origin}/v1/auth/jwt/oidc/auth_url`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
role: role,
|
||||
redirect_uri: `${window.location.origin}/v1/auth/jwt/oidc/callback`
|
||||
})
|
||||
}).then(function(response) {
|
||||
return response.json();
|
||||
}).then(function(myJSON) {
|
||||
oidcAuth(myJSON.data.auth_url);
|
||||
});
|
||||
}
|
||||
|
||||
function oidcAuth(url) {
|
||||
console.log(url);
|
||||
url = url.replace("vaultserver", window.location.origin);
|
||||
console.log(url);
|
||||
location.replace(url);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
74
vendor/github.com/mitchellh/pointerstructure/README.md
generated
vendored
Normal file
74
vendor/github.com/mitchellh/pointerstructure/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# pointerstructure [](https://godoc.org/github.com/mitchellh/pointerstructure)
|
||||
|
||||
pointerstructure is a Go library for identifying a specific value within
|
||||
any Go structure using a string syntax.
|
||||
|
||||
pointerstructure is based on
|
||||
[JSON Pointer (RFC 6901)](https://tools.ietf.org/html/rfc6901), but
|
||||
reimplemented for Go.
|
||||
|
||||
The goal of pointerstructure is to provide a single, well-known format
|
||||
for addressing a specific value. This can be useful for user provided
|
||||
input on structures, diffs of structures, etc.
|
||||
|
||||
## Features
|
||||
|
||||
* Get the value for an address
|
||||
|
||||
* Set the value for an address within an existing structure
|
||||
|
||||
* Delete the value at an address
|
||||
|
||||
* Sorting a list of addresses
|
||||
|
||||
## Installation
|
||||
|
||||
Standard `go get`:
|
||||
|
||||
```
|
||||
$ go get github.com/mitchellh/pointerstructure
|
||||
```
|
||||
|
||||
## Usage & Example
|
||||
|
||||
For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/pointerstructure).
|
||||
|
||||
A quick code example is shown below:
|
||||
|
||||
```go
|
||||
complex := map[string]interface{}{
|
||||
"alice": 42,
|
||||
"bob": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "Bob",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
value, err := pointerstructure.Get(complex, "/bob/0/name")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s", value)
|
||||
// Output:
|
||||
// Bob
|
||||
```
|
||||
|
||||
Continuing the example above, you can also set values:
|
||||
|
||||
```go
|
||||
value, err = pointerstructure.Set(complex, "/bob/0/name", "Alice")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
value, err = pointerstructure.Get(complex, "/bob/0/name")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s", value)
|
||||
// Output:
|
||||
// Alice
|
||||
```
|
||||
112
vendor/github.com/mitchellh/pointerstructure/delete.go
generated
vendored
Normal file
112
vendor/github.com/mitchellh/pointerstructure/delete.go
generated
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
package pointerstructure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Delete deletes the value specified by the pointer p in structure s.
|
||||
//
|
||||
// When deleting a slice index, all other elements will be shifted to
|
||||
// the left. This is specified in RFC6902 (JSON Patch) and not RFC6901 since
|
||||
// RFC6901 doesn't specify operations on pointers. If you don't want to
|
||||
// shift elements, you should use Set to set the slice index to the zero value.
|
||||
//
|
||||
// The structures s must have non-zero values set up to this pointer.
|
||||
// For example, if deleting "/bob/0/name", then "/bob/0" must be set already.
|
||||
//
|
||||
// The returned value is potentially a new value if this pointer represents
|
||||
// the root document. Otherwise, the returned value will always be s.
|
||||
func (p *Pointer) Delete(s interface{}) (interface{}, error) {
|
||||
// if we represent the root doc, we've deleted everything
|
||||
if len(p.Parts) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Save the original since this is going to be our return value
|
||||
originalS := s
|
||||
|
||||
// Get the parent value
|
||||
var err error
|
||||
s, err = p.Parent().Get(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Map for lookup of getter to call for type
|
||||
funcMap := map[reflect.Kind]deleteFunc{
|
||||
reflect.Array: p.deleteSlice,
|
||||
reflect.Map: p.deleteMap,
|
||||
reflect.Slice: p.deleteSlice,
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(s)
|
||||
for val.Kind() == reflect.Interface {
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
for val.Kind() == reflect.Ptr {
|
||||
val = reflect.Indirect(val)
|
||||
}
|
||||
|
||||
f, ok := funcMap[val.Kind()]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("delete %s: invalid value kind: %s", p, val.Kind())
|
||||
}
|
||||
|
||||
result, err := f(originalS, val)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("delete %s: %s", p, err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type deleteFunc func(interface{}, reflect.Value) (interface{}, error)
|
||||
|
||||
func (p *Pointer) deleteMap(root interface{}, m reflect.Value) (interface{}, error) {
|
||||
part := p.Parts[len(p.Parts)-1]
|
||||
key, err := coerce(reflect.ValueOf(part), m.Type().Key())
|
||||
if err != nil {
|
||||
return root, err
|
||||
}
|
||||
|
||||
// Delete the key
|
||||
var elem reflect.Value
|
||||
m.SetMapIndex(key, elem)
|
||||
return root, nil
|
||||
}
|
||||
|
||||
func (p *Pointer) deleteSlice(root interface{}, s reflect.Value) (interface{}, error) {
|
||||
// Coerce the key to an int
|
||||
part := p.Parts[len(p.Parts)-1]
|
||||
idxVal, err := coerce(reflect.ValueOf(part), reflect.TypeOf(42))
|
||||
if err != nil {
|
||||
return root, err
|
||||
}
|
||||
idx := int(idxVal.Int())
|
||||
|
||||
// Verify we're within bounds
|
||||
if idx < 0 || idx >= s.Len() {
|
||||
return root, fmt.Errorf(
|
||||
"index %d is out of range (length = %d)", idx, s.Len())
|
||||
}
|
||||
|
||||
// Mimicing the following with reflection to do this:
|
||||
//
|
||||
// copy(a[i:], a[i+1:])
|
||||
// a[len(a)-1] = nil // or the zero value of T
|
||||
// a = a[:len(a)-1]
|
||||
|
||||
// copy(a[i:], a[i+1:])
|
||||
reflect.Copy(s.Slice(idx, s.Len()), s.Slice(idx+1, s.Len()))
|
||||
|
||||
// a[len(a)-1] = nil // or the zero value of T
|
||||
s.Index(s.Len() - 1).Set(reflect.Zero(s.Type().Elem()))
|
||||
|
||||
// a = a[:len(a)-1]
|
||||
s = s.Slice(0, s.Len()-1)
|
||||
|
||||
// set the slice back on the parent
|
||||
return p.Parent().Set(root, s.Interface())
|
||||
}
|
||||
91
vendor/github.com/mitchellh/pointerstructure/get.go
generated
vendored
Normal file
91
vendor/github.com/mitchellh/pointerstructure/get.go
generated
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
package pointerstructure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Get reads the value out of the total value v.
|
||||
func (p *Pointer) Get(v interface{}) (interface{}, error) {
|
||||
// fast-path the empty address case to avoid reflect.ValueOf below
|
||||
if len(p.Parts) == 0 {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Map for lookup of getter to call for type
|
||||
funcMap := map[reflect.Kind]func(string, reflect.Value) (reflect.Value, error){
|
||||
reflect.Array: p.getSlice,
|
||||
reflect.Map: p.getMap,
|
||||
reflect.Slice: p.getSlice,
|
||||
}
|
||||
|
||||
currentVal := reflect.ValueOf(v)
|
||||
for i, part := range p.Parts {
|
||||
for currentVal.Kind() == reflect.Interface {
|
||||
currentVal = currentVal.Elem()
|
||||
}
|
||||
|
||||
for currentVal.Kind() == reflect.Ptr {
|
||||
currentVal = reflect.Indirect(currentVal)
|
||||
}
|
||||
|
||||
f, ok := funcMap[currentVal.Kind()]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(
|
||||
"%s: at part %d, invalid value kind: %s", p, i, currentVal.Kind())
|
||||
}
|
||||
|
||||
var err error
|
||||
currentVal, err = f(part, currentVal)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s at part %d: %s", p, i, err)
|
||||
}
|
||||
}
|
||||
|
||||
return currentVal.Interface(), nil
|
||||
}
|
||||
|
||||
func (p *Pointer) getMap(part string, m reflect.Value) (reflect.Value, error) {
|
||||
var zeroValue reflect.Value
|
||||
|
||||
// Coerce the string part to the correct key type
|
||||
key, err := coerce(reflect.ValueOf(part), m.Type().Key())
|
||||
if err != nil {
|
||||
return zeroValue, err
|
||||
}
|
||||
|
||||
// Verify that the key exists
|
||||
found := false
|
||||
for _, k := range m.MapKeys() {
|
||||
if k.Interface() == key.Interface() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return zeroValue, fmt.Errorf("couldn't find key %#v", key.Interface())
|
||||
}
|
||||
|
||||
// Get the key
|
||||
return m.MapIndex(key), nil
|
||||
}
|
||||
|
||||
func (p *Pointer) getSlice(part string, v reflect.Value) (reflect.Value, error) {
|
||||
var zeroValue reflect.Value
|
||||
|
||||
// Coerce the key to an int
|
||||
idxVal, err := coerce(reflect.ValueOf(part), reflect.TypeOf(42))
|
||||
if err != nil {
|
||||
return zeroValue, err
|
||||
}
|
||||
idx := int(idxVal.Int())
|
||||
|
||||
// Verify we're within bounds
|
||||
if idx < 0 || idx >= v.Len() {
|
||||
return zeroValue, fmt.Errorf(
|
||||
"index %d is out of range (length = %d)", idx, v.Len())
|
||||
}
|
||||
|
||||
// Get the key
|
||||
return v.Index(idx), nil
|
||||
}
|
||||
57
vendor/github.com/mitchellh/pointerstructure/parse.go
generated
vendored
Normal file
57
vendor/github.com/mitchellh/pointerstructure/parse.go
generated
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package pointerstructure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Parse parses a pointer from the input string. The input string
|
||||
// is expected to follow the format specified by RFC 6901: '/'-separated
|
||||
// parts. Each part can contain escape codes to contain '/' or '~'.
|
||||
func Parse(input string) (*Pointer, error) {
|
||||
// Special case the empty case
|
||||
if input == "" {
|
||||
return &Pointer{}, nil
|
||||
}
|
||||
|
||||
// We expect the first character to be "/"
|
||||
if input[0] != '/' {
|
||||
return nil, fmt.Errorf(
|
||||
"parse Go pointer %q: first char must be '/'", input)
|
||||
}
|
||||
|
||||
// Trim out the first slash so we don't have to +1 every index
|
||||
input = input[1:]
|
||||
|
||||
// Parse out all the parts
|
||||
var parts []string
|
||||
lastSlash := -1
|
||||
for i, r := range input {
|
||||
if r == '/' {
|
||||
parts = append(parts, input[lastSlash+1:i])
|
||||
lastSlash = i
|
||||
}
|
||||
}
|
||||
|
||||
// Add last part
|
||||
parts = append(parts, input[lastSlash+1:])
|
||||
|
||||
// Process each part for string replacement
|
||||
for i, p := range parts {
|
||||
// Replace ~1 followed by ~0 as specified by the RFC
|
||||
parts[i] = strings.Replace(
|
||||
strings.Replace(p, "~1", "/", -1), "~0", "~", -1)
|
||||
}
|
||||
|
||||
return &Pointer{Parts: parts}, nil
|
||||
}
|
||||
|
||||
// MustParse is like Parse but panics if the input cannot be parsed.
|
||||
func MustParse(input string) *Pointer {
|
||||
p, err := Parse(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
123
vendor/github.com/mitchellh/pointerstructure/pointer.go
generated
vendored
Normal file
123
vendor/github.com/mitchellh/pointerstructure/pointer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
// Package pointerstructure provides functions for identifying a specific
|
||||
// value within any Go structure using a string syntax.
|
||||
//
|
||||
// The syntax used is based on JSON Pointer (RFC 6901).
|
||||
package pointerstructure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
// Pointer represents a pointer to a specific value. You can construct
|
||||
// a pointer manually or use Parse.
|
||||
type Pointer struct {
|
||||
// Parts are the pointer parts. No escape codes are processed here.
|
||||
// The values are expected to be exact. If you have escape codes, use
|
||||
// the Parse functions.
|
||||
Parts []string
|
||||
}
|
||||
|
||||
// Get reads the value at the given pointer.
|
||||
//
|
||||
// This is a shorthand for calling Parse on the pointer and then calling Get
|
||||
// on that result. An error will be returned if the value cannot be found or
|
||||
// there is an error with the format of pointer.
|
||||
func Get(value interface{}, pointer string) (interface{}, error) {
|
||||
p, err := Parse(pointer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.Get(value)
|
||||
}
|
||||
|
||||
// Set sets the value at the given pointer.
|
||||
//
|
||||
// This is a shorthand for calling Parse on the pointer and then calling Set
|
||||
// on that result. An error will be returned if the value cannot be found or
|
||||
// there is an error with the format of pointer.
|
||||
//
|
||||
// Set returns the complete document, which might change if the pointer value
|
||||
// points to the root ("").
|
||||
func Set(doc interface{}, pointer string, value interface{}) (interface{}, error) {
|
||||
p, err := Parse(pointer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.Set(doc, value)
|
||||
}
|
||||
|
||||
// String returns the string value that can be sent back to Parse to get
|
||||
// the same Pointer result.
|
||||
func (p *Pointer) String() string {
|
||||
if len(p.Parts) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Copy the parts so we can convert back the escapes
|
||||
result := make([]string, len(p.Parts))
|
||||
copy(result, p.Parts)
|
||||
for i, p := range p.Parts {
|
||||
result[i] = strings.Replace(
|
||||
strings.Replace(p, "~", "~0", -1), "/", "~1", -1)
|
||||
|
||||
}
|
||||
|
||||
return "/" + strings.Join(result, "/")
|
||||
}
|
||||
|
||||
// Parent returns a pointer to the parent element of this pointer.
|
||||
//
|
||||
// If Pointer represents the root (empty parts), a pointer representing
|
||||
// the root is returned. Therefore, to check for the root, IsRoot() should be
|
||||
// called.
|
||||
func (p *Pointer) Parent() *Pointer {
|
||||
// If this is root, then we just return a new root pointer. We allocate
|
||||
// a new one though so this can still be modified.
|
||||
if p.IsRoot() {
|
||||
return &Pointer{}
|
||||
}
|
||||
|
||||
parts := make([]string, len(p.Parts)-1)
|
||||
copy(parts, p.Parts[:len(p.Parts)-1])
|
||||
return &Pointer{
|
||||
Parts: parts,
|
||||
}
|
||||
}
|
||||
|
||||
// IsRoot returns true if this pointer represents the root document.
|
||||
func (p *Pointer) IsRoot() bool {
|
||||
return len(p.Parts) == 0
|
||||
}
|
||||
|
||||
// coerce is a helper to coerce a value to a specific type if it must
|
||||
// and if its possible. If it isn't possible, an error is returned.
|
||||
func coerce(value reflect.Value, to reflect.Type) (reflect.Value, error) {
|
||||
// If the value is already assignable to the type, then let it go
|
||||
if value.Type().AssignableTo(to) {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// If a direct conversion is possible, do that
|
||||
if value.Type().ConvertibleTo(to) {
|
||||
return value.Convert(to), nil
|
||||
}
|
||||
|
||||
// Create a new value to hold our result
|
||||
result := reflect.New(to)
|
||||
|
||||
// Decode
|
||||
if err := mapstructure.WeakDecode(value.Interface(), result.Interface()); err != nil {
|
||||
return result, fmt.Errorf(
|
||||
"couldn't convert value %#v to type %s",
|
||||
value.Interface(), to.String())
|
||||
}
|
||||
|
||||
// We need to indirect the value since reflect.New always creates a pointer
|
||||
return reflect.Indirect(result), nil
|
||||
}
|
||||
122
vendor/github.com/mitchellh/pointerstructure/set.go
generated
vendored
Normal file
122
vendor/github.com/mitchellh/pointerstructure/set.go
generated
vendored
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
package pointerstructure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Set writes a value v to the pointer p in structure s.
|
||||
//
|
||||
// The structures s must have non-zero values set up to this pointer.
|
||||
// For example, if setting "/bob/0/name", then "/bob/0" must be set already.
|
||||
//
|
||||
// The returned value is potentially a new value if this pointer represents
|
||||
// the root document. Otherwise, the returned value will always be s.
|
||||
func (p *Pointer) Set(s, v interface{}) (interface{}, error) {
|
||||
// if we represent the root doc, return that
|
||||
if len(p.Parts) == 0 {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Save the original since this is going to be our return value
|
||||
originalS := s
|
||||
|
||||
// Get the parent value
|
||||
var err error
|
||||
s, err = p.Parent().Get(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Map for lookup of getter to call for type
|
||||
funcMap := map[reflect.Kind]setFunc{
|
||||
reflect.Array: p.setSlice,
|
||||
reflect.Map: p.setMap,
|
||||
reflect.Slice: p.setSlice,
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(s)
|
||||
for val.Kind() == reflect.Interface {
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
for val.Kind() == reflect.Ptr {
|
||||
val = reflect.Indirect(val)
|
||||
}
|
||||
|
||||
f, ok := funcMap[val.Kind()]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("set %s: invalid value kind: %s", p, val.Kind())
|
||||
}
|
||||
|
||||
result, err := f(originalS, val, reflect.ValueOf(v))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("set %s: %s", p, err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type setFunc func(interface{}, reflect.Value, reflect.Value) (interface{}, error)
|
||||
|
||||
func (p *Pointer) setMap(root interface{}, m, value reflect.Value) (interface{}, error) {
|
||||
part := p.Parts[len(p.Parts)-1]
|
||||
key, err := coerce(reflect.ValueOf(part), m.Type().Key())
|
||||
if err != nil {
|
||||
return root, err
|
||||
}
|
||||
|
||||
elem, err := coerce(value, m.Type().Elem())
|
||||
if err != nil {
|
||||
return root, err
|
||||
}
|
||||
|
||||
// Set the key
|
||||
m.SetMapIndex(key, elem)
|
||||
return root, nil
|
||||
}
|
||||
|
||||
func (p *Pointer) setSlice(root interface{}, s, value reflect.Value) (interface{}, error) {
|
||||
// Coerce the value, we'll need that no matter what
|
||||
value, err := coerce(value, s.Type().Elem())
|
||||
if err != nil {
|
||||
return root, err
|
||||
}
|
||||
|
||||
// If the part is the special "-", that means to append it (RFC6901 4.)
|
||||
part := p.Parts[len(p.Parts)-1]
|
||||
if part == "-" {
|
||||
return p.setSliceAppend(root, s, value)
|
||||
}
|
||||
|
||||
// Coerce the key to an int
|
||||
idxVal, err := coerce(reflect.ValueOf(part), reflect.TypeOf(42))
|
||||
if err != nil {
|
||||
return root, err
|
||||
}
|
||||
idx := int(idxVal.Int())
|
||||
|
||||
// Verify we're within bounds
|
||||
if idx < 0 || idx >= s.Len() {
|
||||
return root, fmt.Errorf(
|
||||
"index %d is out of range (length = %d)", idx, s.Len())
|
||||
}
|
||||
|
||||
// Set the key
|
||||
s.Index(idx).Set(value)
|
||||
return root, nil
|
||||
}
|
||||
|
||||
func (p *Pointer) setSliceAppend(root interface{}, s, value reflect.Value) (interface{}, error) {
|
||||
// Coerce the value, we'll need that no matter what. This should
|
||||
// be a no-op since we expect it to be done already, but there is
|
||||
// a fast-path check for that in coerce so do it anyways.
|
||||
value, err := coerce(value, s.Type().Elem())
|
||||
if err != nil {
|
||||
return root, err
|
||||
}
|
||||
|
||||
// We can assume "s" is the parent of pointer value. We need to actually
|
||||
// write s back because Append can return a new slice.
|
||||
return p.Parent().Set(root, reflect.Append(s, value).Interface())
|
||||
}
|
||||
42
vendor/github.com/mitchellh/pointerstructure/sort.go
generated
vendored
Normal file
42
vendor/github.com/mitchellh/pointerstructure/sort.go
generated
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package pointerstructure
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Sort does an in-place sort of the pointers so that they are in order
|
||||
// of least specific to most specific alphabetized. For example:
|
||||
// "/foo", "/foo/0", "/qux"
|
||||
//
|
||||
// This ordering is ideal for applying the changes in a way that ensures
|
||||
// that parents are set first.
|
||||
func Sort(p []*Pointer) { sort.Sort(PointerSlice(p)) }
|
||||
|
||||
// PointerSlice is a slice of pointers that adheres to sort.Interface
|
||||
type PointerSlice []*Pointer
|
||||
|
||||
func (p PointerSlice) Len() int { return len(p) }
|
||||
func (p PointerSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
func (p PointerSlice) Less(i, j int) bool {
|
||||
// Equal number of parts, do a string compare per part
|
||||
for idx, ival := range p[i].Parts {
|
||||
// If we're passed the length of p[j] parts, then we're done
|
||||
if idx >= len(p[j].Parts) {
|
||||
break
|
||||
}
|
||||
|
||||
// Compare the values if they're not equal
|
||||
jval := p[j].Parts[idx]
|
||||
if ival != jval {
|
||||
return ival < jval
|
||||
}
|
||||
}
|
||||
|
||||
// Equal prefix, take the shorter
|
||||
if len(p[i].Parts) != len(p[j].Parts) {
|
||||
return len(p[i].Parts) < len(p[j].Parts)
|
||||
}
|
||||
|
||||
// Equal, it doesn't matter
|
||||
return false
|
||||
}
|
||||
14
vendor/vendor.json
vendored
14
vendor/vendor.json
vendored
|
|
@ -1409,10 +1409,12 @@
|
|||
"revisionTime": "2018-12-10T20:01:33Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "tt3FtyjXgdBI9Mb43UL4LtOZmAk=",
|
||||
"checksumSHA1": "86jzaGc3dRpZ5BKQPFP7ecasQfg=",
|
||||
"path": "github.com/hashicorp/vault-plugin-auth-jwt",
|
||||
"revision": "f428c77917331c1b87dae2dd37016bd1dd4c55da",
|
||||
"revisionTime": "2018-10-31T19:59:42Z"
|
||||
"revision": "bf17a88bb5c43eb2cbdc08011cd76ecec028521c",
|
||||
"revisionTime": "2019-02-07T06:35:46Z",
|
||||
"version": "=oidc-cli",
|
||||
"versionExact": "oidc-cli"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Ldg2jQeyPrpAupyQq4lRVN+jfFY=",
|
||||
|
|
@ -1788,6 +1790,12 @@
|
|||
"revision": "3536a929edddb9a5b34bd6861dc4a9647cb459fe",
|
||||
"revisionTime": "2018-10-05T04:51:35Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "31atAEqGt+z8hZgyVZZokEeM6dM=",
|
||||
"path": "github.com/mitchellh/pointerstructure",
|
||||
"revision": "f2329fcfa9e280bdb5a3f2544aec815a508ad72f",
|
||||
"revisionTime": "2017-02-05T20:42:03Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "nxuST3bjBv5uDVPzrX9wdruOwv0=",
|
||||
"path": "github.com/mitchellh/reflectwalk",
|
||||
|
|
|
|||
Loading…
Reference in a new issue