From 7579ea2430fd9559873c16ac63bac98b4335d506 Mon Sep 17 00:00:00 2001 From: Mike Landau Date: Mon, 18 Sep 2023 20:18:59 -0700 Subject: [PATCH] [auth] Use new auth package --- devbox.go | 4 +- go.mod | 36 ++-- go.sum | 92 ++++++--- internal/auth/auth.go | 313 ----------------------------- internal/auth/authenticator.go | 98 --------- internal/auth/user.go | 89 -------- internal/boxcli/auth.go | 67 +++++- internal/boxcli/pull.go | 24 ++- internal/boxcli/push.go | 16 +- internal/impl/devopt/devboxopts.go | 13 ++ internal/impl/pushpull.go | 9 +- internal/pullbox/config.go | 10 +- internal/pullbox/pullbox.go | 51 +++-- internal/pullbox/s3/config.go | 8 +- internal/pullbox/s3/pull.go | 14 +- internal/pullbox/s3/push.go | 12 +- 16 files changed, 252 insertions(+), 604 deletions(-) delete mode 100644 internal/auth/auth.go delete mode 100644 internal/auth/authenticator.go delete mode 100644 internal/auth/user.go diff --git a/devbox.go b/devbox.go index b8832794064..469b8064705 100644 --- a/devbox.go +++ b/devbox.go @@ -35,8 +35,8 @@ type Devbox interface { PrintEnv(ctx context.Context, includeHooks bool) (string, error) PrintEnvVars(ctx context.Context) ([]string, error) PrintGlobalList() error - Pull(ctx context.Context, overwrite bool, path string) error - Push(ctx context.Context, url string) error + Pull(ctx context.Context, opts devopt.PullboxOpts) error + Push(ctx context.Context, opts devopt.PullboxOpts) error // Remove removes Nix packages from the config so that it no longer exists in // the devbox environment. Remove(ctx context.Context, pkgs ...string) error diff --git a/go.mod b/go.mod index 177680f9470..f12044ef45f 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.21 require ( github.com/AlecAivazis/survey/v2 v2.3.6 github.com/MakeNowJust/heredoc/v2 v2.0.1 - github.com/MicahParks/keyfunc/v2 v2.1.0 github.com/alessio/shellescape v1.4.1 github.com/aws/aws-sdk-go-v2 v1.18.0 github.com/aws/aws-sdk-go-v2/config v1.18.25 @@ -22,21 +21,20 @@ require ( github.com/fatih/color v1.15.0 github.com/fsnotify/fsnotify v1.6.0 github.com/getsentry/sentry-go v0.20.0 - github.com/golang-jwt/jwt/v5 v5.0.0 github.com/google/go-cmp v0.5.9 github.com/google/uuid v1.3.0 github.com/hashicorp/go-envparse v0.1.0 - github.com/mattn/go-isatty v0.0.18 + github.com/mattn/go-isatty v0.0.19 github.com/mholt/archiver/v4 v4.0.0-alpha.7 github.com/pelletier/go-toml/v2 v2.0.7 - github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/errors v0.9.1 - github.com/rogpeppe/go-internal v1.10.0 + github.com/rogpeppe/go-internal v1.11.0 github.com/samber/lo v1.38.1 github.com/segmentio/analytics-go v3.1.0+incompatible - github.com/spf13/cobra v1.6.1 + github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.8.4 github.com/wk8/go-ordered-map/v2 v2.1.8 github.com/zealic/go2node v0.1.0 golang.org/x/exp v0.0.0-20230807204917-050eac23e9de @@ -47,15 +45,27 @@ require ( require ( github.com/joho/godotenv v1.5.1 - golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f + go.jetpack.io/pkg v0.0.0-20230915205515-567047de7b30 + golang.org/x/sync v0.1.0 ) require ( github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect - github.com/kr/pretty v0.1.0 // indirect + github.com/coreos/go-oidc/v3 v3.6.0 // indirect + github.com/go-jose/go-jose/v3 v3.0.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/gosimple/slug v1.13.1 // indirect + github.com/gosimple/unidecode v1.0.1 // indirect + github.com/kr/text v0.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - golang.org/x/net v0.8.0 // indirect + golang.org/x/crypto v0.13.0 // indirect + golang.org/x/net v0.15.0 // indirect + golang.org/x/oauth2 v0.12.0 // indirect + golang.org/x/tools v0.6.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect ) require ( @@ -97,7 +107,7 @@ require ( github.com/therootcompany/xz v1.0.1 // indirect github.com/ulikunitz/xz v0.5.11 // indirect github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/term v0.6.0 // indirect - golang.org/x/text v0.8.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/term v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect ) diff --git a/go.sum b/go.sum index fb11fc2916a..ac96f543d43 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,6 @@ github.com/InVisionApp/go-logger v1.0.1 h1:WFL19PViM1mHUmUWfsv5zMo379KSWj2MRmBlz github.com/InVisionApp/go-logger v1.0.1/go.mod h1:+cGTDSn+P8105aZkeOfIhdd7vFO5X1afUHcjvanY0L8= github.com/MakeNowJust/heredoc/v2 v2.0.1 h1:rlCHh70XXXv7toz95ajQWOWQnN4WNLt0TdpZYIR/J6A= github.com/MakeNowJust/heredoc/v2 v2.0.1/go.mod h1:6/2Abh5s+hc3g9nbWLe9ObDIOhaRrqsyY9MWy+4JdRM= -github.com/MicahParks/keyfunc/v2 v2.1.0 h1:6ZXKb9Rp6qp1bDbJefnG7cTH8yMN1IC/4nf+GVjO99k= -github.com/MicahParks/keyfunc/v2 v2.1.0/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4OeE/yHVMteCkw9k= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= @@ -76,9 +74,12 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cloudflare/ahocorasick v0.0.0-20210425175752-730270c3e184 h1:8yL+85JpbwrIc6m+7N1iYrjn/22z68jwrTIBOJHNe4k= github.com/cloudflare/ahocorasick v0.0.0-20210425175752-730270c3e184/go.mod h1:tGWUZLZp9ajsxUOnHmFFLnqnlKXsCn6GReG4jAD59H0= +github.com/coreos/go-oidc/v3 v3.6.0 h1:AKVxfYw1Gmkn/w96z0DbT/B/xFnzTd3MkZvWLjF4n/o= +github.com/coreos/go-oidc/v3 v3.6.0/go.mod h1:ZpHUsHBucTUj6WOkrP4E20UPynbLZzhTQ1XKCXkxyPc= github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creekorful/mvnparser v1.5.0 h1:tcaof1yFnyzz2t4tWAM7mwYcRLgiHB1Ch5hJHtnBoDk= @@ -102,27 +103,37 @@ github.com/getsentry/sentry-go v0.20.0 h1:bwXW98iMRIWxn+4FgPW7vMrjmbym6HblXALmhj github.com/getsentry/sentry-go v0.20.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= +github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-redis/redis v6.15.5+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= -github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q= +github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= +github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= +github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= github.com/hashicorp/go-envparse v0.1.0 h1:bE++6bhIsNCPLvgDZkYqo3nA+/PFI51pkrHdmPSDFPY= github.com/hashicorp/go-envparse v0.1.0/go.mod h1:OHheN1GoygLlAkTlXLXvAdnXdZxy8JUweQ1rAXx1xnc= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -141,11 +152,10 @@ github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgo github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -155,8 +165,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= @@ -173,14 +183,15 @@ github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc= github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= @@ -195,8 +206,9 @@ github.com/segmentio/backo-go v1.0.1/go.mod h1:9/Rh6yILuLysoQnZ2oNooD2g7aBnvM7r/ github.com/shirou/gopsutil v2.18.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -210,8 +222,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= @@ -225,18 +237,31 @@ github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBU github.com/zaffka/mongodb-boltdb-mock v0.0.0-20221014194232-b4bb03fbe3a0/go.mod h1:GsDD1qsG+86MeeCG7ndi6Ei3iGthKL3wQ7PTFigDfNY= github.com/zealic/go2node v0.1.0 h1:ofxpve08cmLJBwFdI0lPCk9jfwGWOSD+s6216x0oAaA= github.com/zealic/go2node v0.1.0/go.mod h1:GrkFr+HctXwP7vzcU9RsgtAeJjTQ6Ud0IPCQAqpTfBg= +go.jetpack.io/pkg v0.0.0-20230915205515-567047de7b30 h1:s/UMwh3iqQk792t4AhZZtPX0PagGTKwOylOs0cl1gMo= +go.jetpack.io/pkg v0.0.0-20230915205515-567047de7b30/go.mod h1:6RVzBortLFlql8s8oKJTX2+H7DDzp8Lr7wiIOI3FauU= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/exp v0.0.0-20230807204917-050eac23e9de h1:l5Za6utMv/HsBWWqzt4S8X17j+kt1uVETUX5UFhn2rE= golang.org/x/exp v0.0.0-20230807204917-050eac23e9de/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -245,18 +270,30 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= @@ -267,6 +304,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/auth/auth.go b/internal/auth/auth.go deleted file mode 100644 index d8b205b88b6..00000000000 --- a/internal/auth/auth.go +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright 2023 Jetpack Technologies Inc and contributors. All rights reserved. -// Use of this source code is governed by the license in the LICENSE file. - -package auth - -import ( - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "path/filepath" - "strings" - "time" - - "github.com/pkg/browser" - "github.com/pkg/errors" - "go.jetpack.io/devbox/internal/boxcli/usererr" - "go.jetpack.io/devbox/internal/xdg" -) - -var errExpiredOrInvalidRefreshToken = errors.New("refresh token is expired or invalid") - -const additionalSleepOnSlowDown = 1 - -type codeResponse struct { - DeviceCode string `json:"device_code"` - UserCode string `json:"user_code"` - VerificationURI string `json:"verification_uri"` - VerificationURIComplete string `json:"verification_uri_complete"` - ExpiresIn int `json:"expires_in"` - Interval int `json:"interval"` -} - -type tokenSet struct { - AccessToken string `json:"access_token"` - RefreshToken string `json:"refresh_token"` - IDToken string `json:"id_token"` - TokenType string `json:"token_type"` - ExpiresIn int `json:"expires_in"` -} - -// used for both requestToken and refreshToken functions -type requestTokenError struct { - Error string `json:"error"` - ErrorDescription string `json:"error_description"` -} - -// showVerificationURL presents a device flow verification URL to the user, -// either by printing it to stdout or opening a web browser. -func (a *Authenticator) showVerificationURL(url string, w io.Writer) { - err := browser.OpenURL(url) - if err == nil { - fmt.Fprintf(w, "Opening your browser to complete the login. "+ - "If your browser didn't open, you can go to this URL "+ - "and confirm your code manually:\n%s\n\n", url) - return - } - fmt.Fprintf( - w, - "Please go to this URL to confirm this code and login: %s\n\n", url) -} - -// requestDeviceCode requests a device code that the user can use to -// authorize the device. -func (a *Authenticator) requestDeviceCode() (*codeResponse, error) { - reqURL := fmt.Sprintf("https://%s/oauth/device/code", a.Domain) - payload := strings.NewReader(fmt.Sprintf( - "client_id=%s&scope=%s&audience=%s", - a.ClientID, - url.QueryEscape(a.Scope), - a.Audience, - )) - - req, err := http.NewRequest(http.MethodPost, reqURL, payload) - if err != nil { - bytesPayload, _ := io.ReadAll(payload) - return nil, errors.Wrapf( - err, - "failed to send request to URL: %s with payload: %s", - reqURL, - string(bytesPayload), - ) - } - - req.Header.Add("content-type", "application/x-www-form-urlencoded") - - res, err := http.DefaultClient.Do(req) - if err != nil { - return nil, errors.Wrap(err, "failed to send Request") - } - - defer res.Body.Close() - body, err := io.ReadAll(res.Body) - if err != nil { - return nil, errors.Wrap(err, "failed to read response body") - } - - if res.StatusCode != http.StatusOK { - return nil, errors.Errorf( - "got status code: %d, with body %s", - res.StatusCode, - string(body), - ) - } - - response := codeResponse{} - return &response, json.Unmarshal(body, &response) -} - -// requestTokens polls the Auth0 API for tokens. -func (a *Authenticator) requestTokens( - ctx context.Context, - codeResponse *codeResponse, -) (*tokenSet, error) { - - timeToSleep := codeResponse.Interval - ticker := time.NewTicker(time.Duration(timeToSleep) * time.Second) - defer ticker.Stop() - - // numTries is a counter to guard against infinite looping. - // In the normal course: - // Status Code 200 OK: we early return within loop - // Known Error scenarios: we continue looping and requesting Auth0 API. - // These are not "errors" so much as "user hasn't yet completed - // browser login flow" - // Unknown Error scenarios: we early return within loop - - for numTries := 0; numTries < 100; numTries++ { - select { - case <-ctx.Done(): - return nil, errors.WithStack(ctx.Err()) - - case <-ticker.C: - res, err := a.tryRequestToken(codeResponse) - if err != nil { - return nil, errors.WithStack(err) - } - - defer res.Body.Close() - body, err := io.ReadAll(res.Body) - if err != nil { - return nil, errors.WithStack(err) - } - - // Handle success - if res.StatusCode == http.StatusOK { - tokens := tokenSet{} - return &tokens, json.Unmarshal(body, &tokens) - } - - // Handle failure scenarios - moreSleep, err := handleFailure(body, res.StatusCode) - if err != nil { - return nil, errors.WithStack(err) - } - timeToSleep += moreSleep - ticker.Reset(time.Duration(timeToSleep) * time.Second) - } - } - - return nil, usererr.New("max number of tries exceeded") -} - -func (a *Authenticator) doRefreshToken( - refreshToken string, -) (*tokenSet, error) { - - reqURL := fmt.Sprintf("https://%s/oauth/token", a.Domain) - - payload := fmt.Sprintf( - "grant_type=refresh_token&client_id=%s&refresh_token=%s", - a.ClientID, - refreshToken, - ) - payloadReader := strings.NewReader(payload) - - req, err := http.NewRequest(http.MethodPost, reqURL, payloadReader) - if err != nil { - return nil, errors.Wrapf( - err, - "failed to create request to URL: %s, with payload: %s", - reqURL, - payload, - ) - } - - req.Header.Add("content-type", "application/x-www-form-urlencoded") - - res, err := http.DefaultClient.Do(req) - if err != nil { - return nil, errors.Wrapf( - err, - "failed POST request to reqURL: %s, payload: %s ", - reqURL, - payload, - ) - } - - defer res.Body.Close() - body, err := io.ReadAll(res.Body) - if err != nil { - return nil, errors.Wrap(err, "failed to read response body") - } - - if res.StatusCode == http.StatusOK { - tokens := &tokenSet{} - return tokens, json.Unmarshal(body, tokens) - } - - tokenErrorBody := requestTokenError{} - if err := json.Unmarshal(body, &tokenErrorBody); err != nil { - return nil, errors.Wrapf( - err, - "unable to unmarshal requestTokenError from body %s", - body, - ) - } - - // Special case this, since it usually means refresh token is expired. - if tokenErrorBody.Error == "invalid_grant" { - return nil, errExpiredOrInvalidRefreshToken - } - - return nil, errors.Errorf( - "refreshing access token returned an error (%s) with description: %s", - tokenErrorBody.Error, - tokenErrorBody.ErrorDescription, - ) -} - -func (a *Authenticator) tryRequestToken( - codeResponse *codeResponse, -) (*http.Response, error) { - reqURL := fmt.Sprintf("https://%s/oauth/token", a.Domain) - - grantType := "urn:ietf:params:oauth:grant-type:device_code" - payload := strings.NewReader(fmt.Sprintf( - "grant_type=%s&device_code=%s&client_id=%s", - url.QueryEscape(grantType), - codeResponse.DeviceCode, - a.ClientID, - )) - - req, err := http.NewRequest(http.MethodPost, reqURL, payload) - if err != nil { - return nil, errors.WithStack(err) - } - - req.Header.Add("content-type", "application/x-www-form-urlencoded") - - return http.DefaultClient.Do(req) -} - -// handleFailure handles the failure scenarios for requestTokens. -func handleFailure(body []byte, code int) (int, error) { - tokenErrorBody := requestTokenError{} - if err := json.Unmarshal(body, &tokenErrorBody); err != nil { - return 0, errors.WithStack(err) - } - - if code == http.StatusTooManyRequests { - if tokenErrorBody.Error != "slow_down" { - return 0, errors.Errorf( - "got status code: %d, response body: %s", - code, - body, - ) - } - - return additionalSleepOnSlowDown, nil - - } else if code == http.StatusForbidden { - - // this error is received when waiting for user to take action - // when they are logging in via browser. Continue polling. - if tokenErrorBody.Error != "authorization_pending" { - return 0, errors.Errorf( - "got status code: %d, response body: %s", - code, - body, - ) - } - - return 0, nil // No slowdown, just keep trying - } - // The user has not authorized the device quickly enough, so - // the `device_code` has expired. Notify the user that the - // flow has expired and prompt them to re-initiate the flow. - // The "expired_token" is returned exactly once. After that, - // the dreaded "invalid_grant" will be returned and device - // must stop polling. - if tokenErrorBody.Error == "expired_token" || tokenErrorBody.Error == "invalid_grant" { - return 0, usererr.New( - "The device code has expired. Please try `devbox auth login` again.") - } - - // "access_denied" can be received for: - // 1. user refused to authorize the device. - // 2. Auth server denied the transaction. - // 3. A configured Auth0 "rule" denied access - if tokenErrorBody.Error == "access_denied" { - return 0, usererr.New("Access was denied") - } - - // Unknown error - return 0, usererr.New("Unable to login") -} - -func getAuthFilePath() string { - return xdg.StateSubpath(filepath.FromSlash("devbox/auth.json")) -} diff --git a/internal/auth/authenticator.go b/internal/auth/authenticator.go deleted file mode 100644 index e8ad63c2477..00000000000 --- a/internal/auth/authenticator.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2023 Jetpack Technologies Inc and contributors. All rights reserved. -// Use of this source code is governed by the license in the LICENSE file. - -package auth - -import ( - "context" - "fmt" - "io" - "os" - - "github.com/pkg/errors" - "go.jetpack.io/devbox/internal/boxcli/usererr" - "go.jetpack.io/devbox/internal/cuecfg" - "go.jetpack.io/devbox/internal/envir" -) - -// Authenticator performs various auth0 login flows to authenticate users. -type Authenticator struct { - ClientID string - Domain string - Scope string - Audience string -} - -// NewAuthenticator creates an authenticator that uses the auth0 production -// tenancy. -func NewAuthenticator() *Authenticator { - return &Authenticator{ - ClientID: envir.GetValueOrDefault( - "DEVBOX_AUTH_CLIENT_ID", - "5PusB4fMm6BQ8WbTFObkTI0JUDi9ahPC", - ), - Domain: envir.GetValueOrDefault( - "DEVBOX_AUTH_DOMAIN", - "auth.jetpack.io", - ), - Scope: envir.GetValueOrDefault( - "DEVBOX_AUTH_SCOPE", - "openid offline_access email profile", - ), - Audience: envir.GetValueOrDefault( - "DEVBOX_AUTH_AUDIENCE", - "https://api.jetpack.io", - ), - } -} - -// DeviceAuthFlow starts decide auth flow -func (a *Authenticator) DeviceAuthFlow(ctx context.Context, w io.Writer) error { - resp, err := a.requestDeviceCode() - if err != nil { - return err - } - - fmt.Fprintf(w, "\nYour auth code is: %s\n\n", resp.UserCode) - a.showVerificationURL(resp.VerificationURIComplete, w) - - tokenSuccess, err := a.requestTokens(ctx, resp) - if err != nil { - return err - } - - if err = cuecfg.WriteFile(getAuthFilePath(), tokenSuccess); err != nil { - return err - } - - fmt.Fprintln(w, "You are now authenticated.") - return nil -} - -// Use existing refresh tokens to cycle all tokens. This will fail if refresh -// tokens are missing or expired. Handle accordingly -func (a *Authenticator) RefreshTokens() (*tokenSet, error) { - tokens := &tokenSet{} - if err := cuecfg.ParseFile(getAuthFilePath(), tokens); err != nil { - if errors.Is(err, os.ErrNotExist) { - return nil, - usererr.New("You must have previously logged in to use this command") - } - return nil, err - } - - tokens, err := a.doRefreshToken(tokens.RefreshToken) - if err != nil { - if errors.Is(err, errExpiredOrInvalidRefreshToken) { - return nil, usererr.New("Your refresh token is expired or invalid. " + - "Please log in again using `devbox auth login`") - } - return nil, err - } - - return tokens, cuecfg.WriteFile(getAuthFilePath(), tokens) -} - -func (a *Authenticator) Logout() error { - return os.Remove(getAuthFilePath()) -} diff --git a/internal/auth/user.go b/internal/auth/user.go deleted file mode 100644 index 756ea286e18..00000000000 --- a/internal/auth/user.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2023 Jetpack Technologies Inc and contributors. All rights reserved. -// Use of this source code is governed by the license in the LICENSE file. - -package auth - -import ( - "fmt" - "os" - - "github.com/MicahParks/keyfunc/v2" - "github.com/golang-jwt/jwt/v5" - "github.com/pkg/errors" - "go.jetpack.io/devbox/internal/boxcli/usererr" - "go.jetpack.io/devbox/internal/cuecfg" -) - -type User struct { - filesystemTokens *tokenSet - IDToken *jwt.Token -} - -func GetUser() (*User, error) { - filesystemTokens := &tokenSet{} - if err := cuecfg.ParseFile(getAuthFilePath(), filesystemTokens); err != nil { - if errors.Is(err, os.ErrNotExist) { - return nil, usererr.New( - "You must be logged in to use this command. Run `devbox auth login`", - ) - } - return nil, err - } - // Attempt to parse and verify the ID token. - IDToken, err := parseToken(filesystemTokens.IDToken) - if err != nil && !errors.Is(err, jwt.ErrTokenExpired) { - return nil, err - } - - // If the token is expired, refresh the tokens and try again. - if errors.Is(err, jwt.ErrTokenExpired) { - filesystemTokens, err = NewAuthenticator().RefreshTokens() - if err != nil { - return nil, err - } - IDToken, err = parseToken(filesystemTokens.IDToken) - if err != nil { - return nil, err - } - } - - return &User{filesystemTokens: filesystemTokens, IDToken: IDToken}, nil -} - -func (u *User) String() string { - return u.Email() -} - -func (u *User) Email() string { - if u == nil || u.IDToken == nil { - return "" - } - return u.IDToken.Claims.(jwt.MapClaims)["email"].(string) -} - -func (u *User) ID() string { - if u == nil || u.IDToken == nil { - return "" - } - return u.IDToken.Claims.(jwt.MapClaims)["sub"].(string) -} - -func parseToken(stringToken string) (*jwt.Token, error) { - authenticator := NewAuthenticator() - jwksURL := fmt.Sprintf( - "https://%s/.well-known/jwks.json", - authenticator.Domain, - ) - // TODO: Cache this - jwks, err := keyfunc.Get(jwksURL, keyfunc.Options{}) - if err != nil { - return nil, errors.WithStack(err) - } - - token, err := jwt.Parse(stringToken, jwks.Keyfunc) - if err != nil { - return nil, errors.WithStack(err) - } - - return token, nil -} diff --git a/internal/boxcli/auth.go b/internal/boxcli/auth.go index f0fb137838a..a881d1e8ee2 100644 --- a/internal/boxcli/auth.go +++ b/internal/boxcli/auth.go @@ -7,9 +7,15 @@ import ( "fmt" "github.com/spf13/cobra" - "go.jetpack.io/devbox/internal/auth" + "go.jetpack.io/devbox/internal/boxcli/usererr" + "go.jetpack.io/devbox/internal/envir" + "go.jetpack.io/pkg/sandbox/auth" + "go.jetpack.io/pkg/sandbox/auth/session" ) +var issuer = envir.GetValueOrDefault("DEVBOX_AUTH_ISSUER", "https://accounts.jetpack.io") +var clientID = envir.GetValueOrDefault("DEVBOX_AUTH_CLIENT_ID", "ff3d4c9c-1ac8-42d9-bef1-f5218bb1a9f6") + func authCmd() *cobra.Command { cmd := &cobra.Command{ Use: "auth", @@ -30,10 +36,16 @@ func loginCmd() *cobra.Command { Short: "Login to devbox", Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { - return auth.NewAuthenticator().DeviceAuthFlow( - cmd.Context(), - cmd.OutOrStdout(), - ) + c, err := auth.NewClient(issuer, clientID) + if err != nil { + return err + } + t, err := c.LoginFlow() + if err != nil { + return err + } + fmt.Fprintf(cmd.ErrOrStderr(), "Logged in as : %s\n", t.IDClaims().Email) + return nil }, } @@ -46,7 +58,11 @@ func logoutCmd() *cobra.Command { Short: "logout from devbox", Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { - err := auth.NewAuthenticator().Logout() + c, err := auth.NewClient(issuer, clientID) + if err != nil { + return err + } + err = c.LogoutFlow() if err == nil { fmt.Fprintln(cmd.OutOrStdout(), "Logged out successfully") } @@ -65,8 +81,13 @@ func refreshCmd() *cobra.Command { Args: cobra.ExactArgs(0), Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { - _, err := auth.NewAuthenticator().RefreshTokens() - return err + c, err := auth.NewClient(issuer, clientID) + if err != nil { + return err + } + _ = c.RefreshSession() + fmt.Fprintln(cmd.OutOrStdout(), "Refreshed successfully") + return nil }, } @@ -79,14 +100,40 @@ func whoAmICmd() *cobra.Command { Short: "Show the current user", Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { - user, err := auth.GetUser() + tok, err := genSession() if err != nil { return err + } else if tok == nil { + return usererr.New("not logged in") + } + idClaims := tok.IDClaims() + + fmt.Fprintf(cmd.OutOrStdout(), "Logged in\n") + fmt.Fprintf(cmd.OutOrStdout(), "User ID: %s\n", idClaims.Subject) + + if idClaims.OrgID != "" { + fmt.Fprintf(cmd.OutOrStdout(), "Org ID: %s\n", idClaims.OrgID) + } + + if idClaims.Email != "" { + fmt.Fprintf(cmd.OutOrStdout(), "Email: %s\n", idClaims.Email) } - fmt.Fprintln(cmd.OutOrStdout(), user) + + if idClaims.Name != "" { + fmt.Fprintf(cmd.OutOrStdout(), "Name: %s\n", idClaims.Name) + } + return nil }, } return cmd } + +func genSession() (*session.Token, error) { + c, err := auth.NewClient(issuer, clientID) + if err != nil { + return nil, err + } + return c.GetSession(), nil +} diff --git a/internal/boxcli/pull.go b/internal/boxcli/pull.go index b98df40ab0d..12fc565113f 100644 --- a/internal/boxcli/pull.go +++ b/internal/boxcli/pull.go @@ -61,7 +61,23 @@ func pullCmdFunc(cmd *cobra.Command, url string, flags *pullCmdFlags) error { return errors.WithStack(err) } - err = box.Pull(cmd.Context(), flags.force, pullPath) + var creds devopt.Credentials + t, err := genSession() + if err != nil { + return errors.WithStack(err) + } else if t != nil { + creds = devopt.Credentials{ + IDToken: t.IDToken, + Email: t.IDClaims().Email, + Sub: t.IDClaims().ID, + } + } + + err = box.Pull(cmd.Context(), devopt.PullboxOpts{ + URL: pullPath, + Overwrite: flags.force, + Credentials: creds, + }) if prompt := pullErrorPrompt(err); prompt != "" { prompt := &survey.Confirm{Message: prompt} if err = survey.AskOne(prompt, &flags.force); err != nil { @@ -70,7 +86,11 @@ func pullCmdFunc(cmd *cobra.Command, url string, flags *pullCmdFlags) error { if !flags.force { return nil } - err = box.Pull(cmd.Context(), flags.force, pullPath) + err = box.Pull(cmd.Context(), devopt.PullboxOpts{ + URL: pullPath, + Overwrite: flags.force, + Credentials: creds, + }) } if errors.Is(err, s3.ErrProfileNotFound) { return usererr.New( diff --git a/internal/boxcli/push.go b/internal/boxcli/push.go index 8691c1cd2fb..cdf9eeeb099 100644 --- a/internal/boxcli/push.go +++ b/internal/boxcli/push.go @@ -41,5 +41,19 @@ func pushCmdFunc(cmd *cobra.Command, url string, flags pushCmdFlags) error { if err != nil { return errors.WithStack(err) } - return box.Push(cmd.Context(), url) + t, err := genSession() + var creds devopt.Credentials + if err != nil { + return errors.WithStack(err) + } else if t != nil { + creds = devopt.Credentials{ + IDToken: t.IDToken, + Email: t.IDClaims().Email, + Sub: t.IDClaims().ID, + } + } + return box.Push(cmd.Context(), devopt.PullboxOpts{ + URL: url, + Credentials: creds, + }) } diff --git a/internal/impl/devopt/devboxopts.go b/internal/impl/devopt/devboxopts.go index e98578b5903..87a4a5dd570 100644 --- a/internal/impl/devopt/devboxopts.go +++ b/internal/impl/devopt/devboxopts.go @@ -24,3 +24,16 @@ type EnvFlags struct { EnvMap map[string]string EnvFile string } + +type PullboxOpts struct { + Overwrite bool + URL string + Credentials Credentials +} + +type Credentials struct { + IDToken string + // TODO We can just parse these out, but don't want to add a dependency right now + Email string + Sub string +} diff --git a/internal/impl/pushpull.go b/internal/impl/pushpull.go index 801da1c4ba2..5d20d85784b 100644 --- a/internal/impl/pushpull.go +++ b/internal/impl/pushpull.go @@ -7,17 +7,18 @@ import ( "context" "runtime/trace" + "go.jetpack.io/devbox/internal/impl/devopt" "go.jetpack.io/devbox/internal/pullbox" ) -func (d *Devbox) Pull(ctx context.Context, force bool, path string) error { +func (d *Devbox) Pull(ctx context.Context, opts devopt.PullboxOpts) error { ctx, task := trace.NewTask(ctx, "devboxPull") defer task.End() - return pullbox.New(d, path, force).Pull(ctx) + return pullbox.New(d, opts).Pull(ctx) } -func (d *Devbox) Push(ctx context.Context, url string) error { +func (d *Devbox) Push(ctx context.Context, opts devopt.PullboxOpts) error { ctx, task := trace.NewTask(ctx, "devboxPush") defer task.End() - return pullbox.New(d, url, false).Push(ctx) + return pullbox.New(d, opts).Push(ctx) } diff --git a/internal/pullbox/config.go b/internal/pullbox/config.go index 298741bac85..e6165554df3 100644 --- a/internal/pullbox/config.go +++ b/internal/pullbox/config.go @@ -14,21 +14,21 @@ import ( ) func (p *pullbox) IsTextDevboxConfig() bool { - if u, err := url.Parse(p.url); err == nil { + if u, err := url.Parse(p.URL); err == nil { ext := filepath.Ext(u.Path) return cuecfg.IsSupportedExtension(ext) } // For invalid URLS, just look at the extension - ext := filepath.Ext(p.url) + ext := filepath.Ext(p.URL) return cuecfg.IsSupportedExtension(ext) } func (p *pullbox) pullTextDevboxConfig() error { if p.isLocalConfig() { - return p.copyToProfile(p.url) + return p.copyToProfile(p.URL) } - cfg, err := devconfig.LoadConfigFromURL(p.url) + cfg, err := devconfig.LoadConfigFromURL(p.URL) if err != nil { return err } @@ -45,6 +45,6 @@ func (p *pullbox) pullTextDevboxConfig() error { } func (p *pullbox) isLocalConfig() bool { - _, err := os.Stat(p.url) + _, err := os.Stat(p.URL) return err == nil } diff --git a/internal/pullbox/pullbox.go b/internal/pullbox/pullbox.go index dee0488d8ce..bd9a5aca902 100644 --- a/internal/pullbox/pullbox.go +++ b/internal/pullbox/pullbox.go @@ -12,8 +12,8 @@ import ( "github.com/pkg/errors" - "go.jetpack.io/devbox/internal/auth" "go.jetpack.io/devbox/internal/boxcli/usererr" + "go.jetpack.io/devbox/internal/impl/devopt" "go.jetpack.io/devbox/internal/pullbox/git" "go.jetpack.io/devbox/internal/pullbox/s3" "go.jetpack.io/devbox/internal/pullbox/tar" @@ -26,12 +26,11 @@ type devboxProject interface { type pullbox struct { devboxProject - overwrite bool - url string + devopt.PullboxOpts } -func New(devbox devboxProject, url string, overwrite bool) *pullbox { - return &pullbox{devbox, overwrite, url} +func New(devbox devboxProject, opts devopt.PullboxOpts) *pullbox { + return &pullbox{devbox, opts} } // Pull @@ -46,32 +45,31 @@ func (p *pullbox) Pull(ctx context.Context) error { notEmpty, err := profileIsNotEmpty(p.ProjectDir()) if err != nil { return err - } else if notEmpty && !p.overwrite { + } else if notEmpty && !p.Overwrite { return fs.ErrExist } - if p.url != "" { - ux.Finfo(os.Stderr, "Pulling global config from %s\n", p.url) + if p.URL != "" { + ux.Finfo(os.Stderr, "Pulling global config from %s\n", p.URL) } else { ux.Finfo(os.Stderr, "Pulling global config\n") } var tmpDir string - if p.url == "" { - user, err := auth.GetUser() - if err != nil { - return err + if p.URL == "" { + if p.Credentials.IDToken == "" { + return usererr.New("Not logged in") } profile := "default" // TODO: make this editable - if tmpDir, err = s3.PullToTmp(ctx, user, profile); err != nil { + if tmpDir, err = s3.PullToTmp(ctx, &p.Credentials, profile); err != nil { return err } return p.copyToProfile(tmpDir) } - if git.IsRepoURL(p.url) { - if tmpDir, err = git.CloneToTmp(p.url); err != nil { + if git.IsRepoURL(p.URL) { + if tmpDir, err = git.CloneToTmp(p.URL); err != nil { return err } // Remove the .git directory, we don't want to keep state @@ -85,10 +83,10 @@ func (p *pullbox) Pull(ctx context.Context) error { return p.pullTextDevboxConfig() } - if isArchive, err := urlIsArchive(p.url); err != nil { + if isArchive, err := urlIsArchive(p.URL); err != nil { return err } else if isArchive { - data, err := download(p.url) + data, err := download(p.URL) if err != nil { return err } @@ -100,29 +98,28 @@ func (p *pullbox) Pull(ctx context.Context) error { return p.copyToProfile(tmpDir) } - return usererr.New("Could not determine how to pull %s", p.url) + return usererr.New("Could not determine how to pull %s", p.URL) } func (p *pullbox) Push(ctx context.Context) error { - if p.url != "" { - ux.Finfo(os.Stderr, "Pushing global config to %s\n", p.url) + if p.URL != "" { + ux.Finfo(os.Stderr, "Pushing global config to %s\n", p.URL) } else { ux.Finfo(os.Stderr, "Pushing global config\n") } - if p.url == "" { + if p.URL == "" { profile := "default" // TODO: make this editable - user, err := auth.GetUser() - if err != nil { - return err + if p.Credentials.IDToken == "" { + return usererr.New("Not logged in") } ux.Finfo( os.Stderr, "Logged in as %s, pushing to to devbox cloud (profile: %s)\n", - user.Email(), + p.Credentials.Email, profile, ) - return s3.Push(ctx, user, p.ProjectDir(), profile) + return s3.Push(ctx, &p.Credentials, p.ProjectDir(), profile) } - return git.Push(ctx, p.ProjectDir(), p.url) + return git.Push(ctx, p.ProjectDir(), p.URL) } diff --git a/internal/pullbox/s3/config.go b/internal/pullbox/s3/config.go index 0d59a8fea2a..fd39f089cda 100644 --- a/internal/pullbox/s3/config.go +++ b/internal/pullbox/s3/config.go @@ -8,7 +8,7 @@ import ( "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/sts" "github.com/pkg/errors" - "go.jetpack.io/devbox/internal/auth" + "go.jetpack.io/devbox/internal/impl/devopt" ) // TODO(landau): We could make these customizable so folks can use their own @@ -22,15 +22,15 @@ const ( region = "us-east-2" ) -func assumeRole(ctx context.Context, user *auth.User) (*aws.Config, error) { +func assumeRole(ctx context.Context, c *devopt.Credentials) (*aws.Config, error) { noPermsConfig, _ := config.LoadDefaultConfig(ctx) stsClient := sts.NewFromConfig(noPermsConfig) creds, err := stsClient.AssumeRoleWithWebIdentity( ctx, &sts.AssumeRoleWithWebIdentityInput{ RoleArn: aws.String(roleArn), - RoleSessionName: aws.String(user.Email()), - WebIdentityToken: aws.String(user.IDToken.Raw), + RoleSessionName: aws.String(c.Email), + WebIdentityToken: aws.String(c.IDToken), }, ) if err != nil { diff --git a/internal/pullbox/s3/pull.go b/internal/pullbox/s3/pull.go index 9c2f600a673..13c3d58faf7 100644 --- a/internal/pullbox/s3/pull.go +++ b/internal/pullbox/s3/pull.go @@ -13,15 +13,19 @@ import ( "github.com/aws/aws-sdk-go-v2/feature/s3/manager" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/pkg/errors" - "go.jetpack.io/devbox/internal/auth" + "go.jetpack.io/devbox/internal/impl/devopt" "go.jetpack.io/devbox/internal/pullbox/tar" "go.jetpack.io/devbox/internal/ux" ) var ErrProfileNotFound = errors.New("profile not found") -func PullToTmp(ctx context.Context, user *auth.User, profile string) (string, error) { - config, err := assumeRole(ctx, user) +func PullToTmp( + ctx context.Context, + creds *devopt.Credentials, + profile string, +) (string, error) { + config, err := assumeRole(ctx, creds) if err != nil { return "", err @@ -34,7 +38,7 @@ func PullToTmp(ctx context.Context, user *auth.User, profile string) (string, er ux.Finfo( os.Stderr, "Logged in as %s, pulling from jetpack cloud (profile: %s)\n", - user.Email(), + creds.Email, profile, ) @@ -46,7 +50,7 @@ func PullToTmp(ctx context.Context, user *auth.User, profile string) (string, er Key: aws.String( fmt.Sprintf( "profiles/%s/%s.tar.gz", - user.ID(), + creds.Sub, profile, ), ), diff --git a/internal/pullbox/s3/push.go b/internal/pullbox/s3/push.go index e41e0c71f23..e3b55d82d04 100644 --- a/internal/pullbox/s3/push.go +++ b/internal/pullbox/s3/push.go @@ -12,18 +12,22 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/feature/s3/manager" "github.com/aws/aws-sdk-go-v2/service/s3" - "go.jetpack.io/devbox/internal/auth" + "go.jetpack.io/devbox/internal/impl/devopt" "go.jetpack.io/devbox/internal/pullbox/tar" "go.jetpack.io/devbox/internal/ux" ) -func Push(ctx context.Context, user *auth.User, dir, profile string) error { +func Push( + ctx context.Context, + creds *devopt.Credentials, + dir, profile string, +) error { archivePath, err := tar.Compress(dir) if err != nil { return err } - config, err := assumeRole(ctx, user) + config, err := assumeRole(ctx, creds) if err != nil { return err } @@ -39,7 +43,7 @@ func Push(ctx context.Context, user *auth.User, dir, profile string) error { Key: aws.String( fmt.Sprintf( "profiles/%s/%s.tar.gz", - user.ID(), + creds.Sub, profile, ), ),