From bf813877153e2eb10365c025b2e581ac4d02f3ad Mon Sep 17 00:00:00 2001 From: Tyler Creller Date: Fri, 5 Jan 2024 11:35:10 -0500 Subject: [PATCH 1/9] OCM-4964: Secure store config --- Makefile | 2 +- go.mod | 10 ++++++++-- go.sum | 8 ++++++++ pkg/config/config.go | 41 ++++++++++++++++++++++++++++++++++++----- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 658c37c0..5f87fc11 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ export GO111MODULE=on export GOPROXY=https://proxy.golang.org # Disable CGO so that we always generate static binaries: -export CGO_ENABLED=0 +export CGO_ENABLED=1 # Allow overriding: `make lint container_runner=docker`. container_runner:=podman diff --git a/go.mod b/go.mod index 9e0b67b7..f16fac3e 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,8 @@ require ( gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a golang.org/x/term v0.15.0 golang.org/x/text v0.14.0 + golang.org/x/term v0.15.0 + golang.org/x/text v0.14.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/apimachinery v0.27.3 ) @@ -31,6 +33,8 @@ require ( github.com/briandowns/spinner v1.19.0 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/danieljoos/wincred v1.2.0 // indirect + github.com/docker/docker-credential-helpers v0.8.0 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fatih/color v1.13.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect @@ -55,6 +59,7 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.16 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect @@ -73,11 +78,12 @@ require ( github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect github.com/zgalor/weberr v0.7.0 // indirect golang.org/x/crypto v0.17.0 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/oauth2 v0.15.0 // indirect + golang.org/x/net v0.10.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/tools v0.9.3 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) + +replace github.com/openshift-online/ocm-sdk-go v0.1.388 => ../ocm-sdk-go diff --git a/go.sum b/go.sum index 7fdfe2e4..bb248e86 100644 --- a/go.sum +++ b/go.sum @@ -73,9 +73,13 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE= +github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8= +github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -252,6 +256,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= +github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -378,6 +384,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= diff --git a/pkg/config/config.go b/pkg/config/config.go index 8284af97..e0df3c91 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -29,6 +29,7 @@ import ( "github.com/golang/glog" homedir "github.com/mitchellh/go-homedir" sdk "github.com/openshift-online/ocm-sdk-go" + "github.com/openshift-online/ocm-sdk-go/authentication/securestore" "github.com/openshift-online/ocm-cli/pkg/debug" "github.com/openshift-online/ocm-cli/pkg/info" @@ -53,13 +54,17 @@ type Config struct { Pager string `json:"pager,omitempty" doc:"Pager command, for example 'less'. If empty no pager will be used."` } -// Load loads the configuration from the configuration file. If the configuration file doesn't exist +// Loads the configuration from the configuration file. If the configuration file doesn't exist // it will return an empty configuration object. func Load() (cfg *Config, err error) { file, err := Location() if err != nil { return } + + if ocmCfg := os.Getenv("OCM_CONFIG"); ocmCfg == securestore.SecureStoreConfigKey { + return LoadFromOS() + } _, err = os.Stat(file) if os.IsNotExist(err) { cfg = &Config{} @@ -88,21 +93,47 @@ func Load() (cfg *Config, err error) { return } +// Loads the configuration from the OS keyring. If the configuration file doesn't exist +// it will return an empty configuration object. +func LoadFromOS() (cfg *Config, err error) { + data, err := securestore.GetConfigFromKeyring() + if err != nil || len(data) == 0 { + return cfg, err + } + + cfg = &Config{} + if len(data) == 0 { + return + } + err = json.Unmarshal(data, cfg) + if err != nil { + err = fmt.Errorf("can't parse config: %v", err) + return + } + return +} + // Save saves the given configuration to the configuration file. func Save(cfg *Config) error { file, err := Location() if err != nil { return err } + + data, err := json.MarshalIndent(cfg, "", " ") + if err != nil { + return fmt.Errorf("can't marshal config: %v", err) + } + + if ocmCfg := os.Getenv("OCM_CONFIG"); ocmCfg == securestore.SecureStoreConfigKey { + return securestore.UpsertConfigToKeyring(data) + } + dir := filepath.Dir(file) err = os.MkdirAll(dir, os.FileMode(0755)) if err != nil { return fmt.Errorf("can't create directory %s: %v", dir, err) } - data, err := json.MarshalIndent(cfg, "", " ") - if err != nil { - return fmt.Errorf("can't marshal config: %v", err) - } err = os.WriteFile(file, data, 0600) if err != nil { return fmt.Errorf("can't write file '%s': %v", file, err) From c81587f2695f836e7fe7f58c73bba4649e436f46 Mon Sep 17 00:00:00 2001 From: Tyler Creller Date: Thu, 11 Jan 2024 15:05:42 -0600 Subject: [PATCH 2/9] OCM-4964: Go mod tidy --- go.mod | 7 ++++++- go.sum | 21 ++++++++++++++++----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index f16fac3e..9e7dcb67 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,8 @@ require ( ) require ( + github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect + github.com/99designs/keyring v1.2.2 // indirect github.com/aws/aws-sdk-go v1.44.110 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -35,16 +37,19 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/danieljoos/wincred v1.2.0 // indirect github.com/docker/docker-credential-helpers v0.8.0 // indirect + github.com/dvsekhvalnov/jose2go v1.5.0 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fatih/color v1.13.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/css v1.0.0 // indirect + github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/itchyny/gojq v0.12.9 // indirect github.com/itchyny/timefmt-go v0.1.4 // indirect @@ -67,13 +72,13 @@ require ( github.com/microcosm-cc/bluemonday v1.0.23 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mtibben/percent v0.2.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.13.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect - github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect github.com/zgalor/weberr v0.7.0 // indirect diff --git a/go.sum b/go.sum index bb248e86..302e62bc 100644 --- a/go.sum +++ b/go.sum @@ -31,6 +31,10 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= +github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0= +github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk= github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -80,6 +84,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8= github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40= +github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM= +github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -108,6 +114,8 @@ github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -178,6 +186,8 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -264,8 +274,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxv github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= @@ -310,8 +318,12 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= +github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nwidger/jsoncolor v0.3.2 h1:rVJJlwAWDJShnbTYOQ5RM7yTA20INyKXlJ/fg4JMhHQ= github.com/nwidger/jsoncolor v0.3.2/go.mod h1:Cs34umxLbJvgBMnVNVqhji9BhoT/N/KinHqZptQ7cf4= github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= @@ -358,8 +370,6 @@ github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0ua github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -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/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= @@ -731,8 +741,9 @@ google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From fa20e8fc01dbf7aa1aeb46c47167760e065dd574 Mon Sep 17 00:00:00 2001 From: Tyler Creller Date: Fri, 12 Jan 2024 10:45:08 -0500 Subject: [PATCH 3/9] OCM-4964: go mod tidy --- go.mod | 2 -- go.sum | 4 ---- 2 files changed, 6 deletions(-) diff --git a/go.mod b/go.mod index 9e7dcb67..115f00e8 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,6 @@ require ( github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/danieljoos/wincred v1.2.0 // indirect - github.com/docker/docker-credential-helpers v0.8.0 // indirect github.com/dvsekhvalnov/jose2go v1.5.0 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fatih/color v1.13.0 // indirect @@ -64,7 +63,6 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect - github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.16 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect diff --git a/go.sum b/go.sum index 302e62bc..629a32f4 100644 --- a/go.sum +++ b/go.sum @@ -82,8 +82,6 @@ github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0S github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8= -github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40= github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM= github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -266,8 +264,6 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= -github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= From e2e6b085289d24ce940cf2541dfebf9c5c0b1391 Mon Sep 17 00:00:00 2001 From: Tyler Creller Date: Fri, 26 Jan 2024 08:20:28 -0500 Subject: [PATCH 4/9] OCM-4964: Secure store updates --- cmd/ocm/login/cmd.go | 4 ++++ go.mod | 5 ++--- pkg/config/config.go | 32 ++++++++++++++++++++++++++------ 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/cmd/ocm/login/cmd.go b/cmd/ocm/login/cmd.go index b5798fd4..fa75616e 100644 --- a/cmd/ocm/login/cmd.go +++ b/cmd/ocm/login/cmd.go @@ -255,6 +255,10 @@ func run(cmd *cobra.Command, argv []string) error { cfg = new(config.Config) } + if args.useAuthCode { + cfg.AuthFlow = string(config.AuthCode) + } + if haveToken { // Encrypted tokens are assumed to be refresh tokens: if config.IsEncryptedToken(args.token) { diff --git a/go.mod b/go.mod index 115f00e8..3449f986 100644 --- a/go.mod +++ b/go.mod @@ -20,8 +20,6 @@ require ( gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a golang.org/x/term v0.15.0 golang.org/x/text v0.14.0 - golang.org/x/term v0.15.0 - golang.org/x/text v0.14.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/apimachinery v0.27.3 ) @@ -81,7 +79,8 @@ require ( github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect github.com/zgalor/weberr v0.7.0 // indirect golang.org/x/crypto v0.17.0 // indirect - golang.org/x/net v0.10.0 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/oauth2 v0.15.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/tools v0.9.3 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/pkg/config/config.go b/pkg/config/config.go index e0df3c91..aae1da23 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -52,8 +52,18 @@ type Config struct { URL string `json:"url,omitempty" doc:"URL of the API gateway. The value can be the complete URL or an alias. The valid aliases are 'production', 'staging' and 'integration'."` User string `json:"user,omitempty" doc:"User name."` Pager string `json:"pager,omitempty" doc:"Pager command, for example 'less'. If empty no pager will be used."` + AuthFlow string `json:"auth,omitempty" doc:"Authentication method to use. Valid values are 'token', 'client', 'auth-code' and 'device-code'"` } +type AuthFlowTypes string + +const ( + ClientAuth AuthFlowTypes = "client" + TokenAuth AuthFlowTypes = "token" + AuthCode AuthFlowTypes = "auth-code" + DeviceCode AuthFlowTypes = "device-code" +) + // Loads the configuration from the configuration file. If the configuration file doesn't exist // it will return an empty configuration object. func Load() (cfg *Config, err error) { @@ -62,7 +72,11 @@ func Load() (cfg *Config, err error) { return } - if ocmCfg := os.Getenv("OCM_CONFIG"); ocmCfg == securestore.SecureStoreConfigKey { + // If the OCM_CONFIG env var is set to `securestore`, + // or auth code/device code flow is being used, + // load from the OS keyring instead + ocmCfg := os.Getenv("OCM_CONFIG") + if ocmCfg == securestore.SecureStoreConfigKey || ocmCfg == string(AuthCode) || ocmCfg == string(DeviceCode) { return LoadFromOS() } _, err = os.Stat(file) @@ -103,14 +117,16 @@ func LoadFromOS() (cfg *Config, err error) { cfg = &Config{} if len(data) == 0 { - return + return cfg, nil } err = json.Unmarshal(data, cfg) if err != nil { - err = fmt.Errorf("can't parse config: %v", err) - return + // Clear the config from the keyring if it's invalid + // treat it as if it doesn't exist so a new one can be created + securestore.UpsertConfigToKeyring([]byte{}) + return cfg, nil } - return + return cfg, nil } // Save saves the given configuration to the configuration file. @@ -125,7 +141,11 @@ func Save(cfg *Config) error { return fmt.Errorf("can't marshal config: %v", err) } - if ocmCfg := os.Getenv("OCM_CONFIG"); ocmCfg == securestore.SecureStoreConfigKey { + // If the OCM_CONFIG env var is set to `securestore`, + // or auth code/device code flow is being used, + // save to the OS keyring instead + ocmCfg := os.Getenv("OCM_CONFIG") + if ocmCfg == securestore.SecureStoreConfigKey || ocmCfg == string(AuthCode) || ocmCfg == string(DeviceCode) { return securestore.UpsertConfigToKeyring(data) } From 896b3a2d6563b781a7a055b200cfc11b2d6d62c7 Mon Sep 17 00:00:00 2001 From: Tyler Creller Date: Tue, 6 Feb 2024 15:22:54 -0500 Subject: [PATCH 5/9] OCM-4965: Use keyring for oauth flows --- Makefile | 7 +- cmd/ocm/config/cmd.go | 2 + cmd/ocm/config/delete/delete.go | 56 ++++++++++++++ cmd/ocm/config/get/get.go | 11 +++ cmd/ocm/login/cmd.go | 32 +++++--- go.mod | 4 +- go.sum | 4 +- pkg/config/config.go | 127 ++++++++++++++++++++++---------- 8 files changed, 186 insertions(+), 57 deletions(-) create mode 100644 cmd/ocm/config/delete/delete.go diff --git a/Makefile b/Makefile index 5f87fc11..96720932 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ export GO111MODULE=on export GOPROXY=https://proxy.golang.org # Disable CGO so that we always generate static binaries: -export CGO_ENABLED=1 +export CGO_ENABLED=0 # Allow overriding: `make lint container_runner=docker`. container_runner:=podman @@ -37,6 +37,11 @@ cmds: install: go install ./cmd/ocm +# CGO_ENABLED=1 is required for Keychain support on macOS +.PHONY: install-cgo +install-cgo: + CGO_ENABLED=1 go install ./cmd/ocm + .PHONY: test tests test tests: cmds ginkgo run -r diff --git a/cmd/ocm/config/cmd.go b/cmd/ocm/config/cmd.go index 2db698da..6ffe14a3 100644 --- a/cmd/ocm/config/cmd.go +++ b/cmd/ocm/config/cmd.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/cobra" + "github.com/openshift-online/ocm-cli/cmd/ocm/config/delete" "github.com/openshift-online/ocm-cli/cmd/ocm/config/get" "github.com/openshift-online/ocm-cli/cmd/ocm/config/set" "github.com/openshift-online/ocm-cli/pkg/config" @@ -73,4 +74,5 @@ var Cmd = &cobra.Command{ func init() { Cmd.AddCommand(get.Cmd) Cmd.AddCommand(set.Cmd) + Cmd.AddCommand(delete.Cmd) } diff --git a/cmd/ocm/config/delete/delete.go b/cmd/ocm/config/delete/delete.go new file mode 100644 index 00000000..d66e968d --- /dev/null +++ b/cmd/ocm/config/delete/delete.go @@ -0,0 +1,56 @@ +/* +Copyright (c) 2024 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package delete + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/openshift-online/ocm-sdk-go/authentication/securestore" +) + +var args struct { + debug bool +} + +var Cmd = &cobra.Command{ + Use: "delete", + Short: "Deletes the existing configuration from the OS keyring", + Long: "Deletes the existing configuration from the OS keyring", + Args: cobra.ExactArgs(0), + RunE: run, + Hidden: true, +} + +func init() { + flags := Cmd.Flags() + flags.BoolVar( + &args.debug, + "debug", + false, + "Enable debug mode.", + ) +} + +func run(cmd *cobra.Command, argv []string) error { + err := securestore.RemoveConfigFromKeyring() + if err != nil { + return fmt.Errorf("can't delete config from keyring: %v", err) + } + return nil +} diff --git a/cmd/ocm/config/get/get.go b/cmd/ocm/config/get/get.go index a9dc002f..47da4cad 100644 --- a/cmd/ocm/config/get/get.go +++ b/cmd/ocm/config/get/get.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/cobra" "github.com/openshift-online/ocm-cli/pkg/config" + "github.com/openshift-online/ocm-sdk-go/authentication/securestore" ) var args struct { @@ -83,9 +84,19 @@ func run(cmd *cobra.Command, argv []string) error { fmt.Fprintf(os.Stdout, "%s\n", cfg.URL) case "pager": fmt.Fprintf(os.Stdout, "%s\n", cfg.Pager) + case "keyrings": + fmt.Fprintf(os.Stdout, "%s\n", getKeyrings()) default: return fmt.Errorf("Unknown setting") } return nil } + +func getKeyrings() []string { + backends := securestore.AvailableBackends() + if len(backends) == 0 { + fmt.Printf("No keyrings available: %s\n", securestore.ErrNoBackendsAvailable) + } + return backends +} diff --git a/cmd/ocm/login/cmd.go b/cmd/ocm/login/cmd.go index fa75616e..1d1bdd28 100644 --- a/cmd/ocm/login/cmd.go +++ b/cmd/ocm/login/cmd.go @@ -220,9 +220,9 @@ func run(cmd *cobra.Command, argv []string) error { // Check that we have some kind of credentials: havePassword := args.user != "" && args.password != "" - haveSecret := args.clientID != "" && args.clientSecret != "" + haveClientCreds := args.clientID != "" && args.clientSecret != "" haveToken := args.token != "" - if !havePassword && !haveSecret && !haveToken { + if !havePassword && !haveClientCreds && !haveToken { // Allow bare `ocm login` to suggest the token page without noise of full help. fmt.Fprintf( os.Stderr, @@ -246,18 +246,28 @@ func run(cmd *cobra.Command, argv []string) error { ) } - // Load the configuration file: - cfg, err := config.Load() + // Set the authentication method: + var authMethod config.AuthMethodType + if args.useAuthCode { + authMethod = config.AuthCode + } else if args.useDeviceCode { + authMethod = config.DeviceCode + } else if haveClientCreds { + authMethod = config.ClientAuth + } else { + authMethod = config.TokenAuth + } + + // Load the configuration: + cfg, err := config.LoginLoad(authMethod) if err != nil { - return fmt.Errorf("Can't load config file: %v", err) + return fmt.Errorf("Can't load config: %v", err) } if cfg == nil { cfg = new(config.Config) } - if args.useAuthCode { - cfg.AuthFlow = string(config.AuthCode) - } + cfg.AuthMethod = string(authMethod) if haveToken { // Encrypted tokens are assumed to be refresh tokens: @@ -346,7 +356,7 @@ func run(cmd *cobra.Command, argv []string) error { err = config.Save(cfg) if err != nil { - return fmt.Errorf("Can't save config file: %v", err) + return fmt.Errorf("can't save config: %v", err) } if args.useAuthCode || args.useDeviceCode { @@ -357,8 +367,8 @@ func run(cmd *cobra.Command, argv []string) error { ssoHost := ssoURL.Scheme + "://" + ssoURL.Hostname() fmt.Println("Login successful") - fmt.Printf("To switch accounts, logout from %s and run `ocm logout` "+ - "before attempting to login again", ssoHost) + fmt.Println(fmt.Sprintf("To switch accounts, logout from %s and run `ocm logout` "+ + "before attempting to login again", ssoHost)) } return nil diff --git a/go.mod b/go.mod index 3449f986..a5516b72 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/nwidger/jsoncolor v0.3.2 github.com/onsi/ginkgo/v2 v2.11.0 github.com/onsi/gomega v1.27.8 - github.com/openshift-online/ocm-sdk-go v0.1.398 + github.com/openshift-online/ocm-sdk-go v0.1.399 github.com/openshift/rosa v1.2.24 github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 github.com/spf13/cobra v1.7.0 @@ -87,5 +87,3 @@ require ( google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) - -replace github.com/openshift-online/ocm-sdk-go v0.1.388 => ../ocm-sdk-go diff --git a/go.sum b/go.sum index 629a32f4..12760244 100644 --- a/go.sum +++ b/go.sum @@ -326,8 +326,8 @@ github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= -github.com/openshift-online/ocm-sdk-go v0.1.398 h1:6C1mDcPxzG4jSduOaWixTTI5gSEO+Jm7OW/00jVoWGI= -github.com/openshift-online/ocm-sdk-go v0.1.398/go.mod h1:tke8vKcE7eHKyRbkJv6qo4ljo919zhx04uyQTcgF5cQ= +github.com/openshift-online/ocm-sdk-go v0.1.399 h1:mmhX+vaB7+aeq5MMZwe9gJhCbWu63GXwpnW+EE+uiv0= +github.com/openshift-online/ocm-sdk-go v0.1.399/go.mod h1:tke8vKcE7eHKyRbkJv6qo4ljo919zhx04uyQTcgF5cQ= github.com/openshift/rosa v1.2.24 h1:vv0yYnWHx6CCPEAau/0rS54P2ksaf+uWXb1TQPWxiYE= github.com/openshift/rosa v1.2.24/go.mod h1:MVXB27O3PF8WoOic23I03mmq6/9kVxpFx6FKyLMCyrQ= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= diff --git a/pkg/config/config.go b/pkg/config/config.go index aae1da23..abe510b0 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -52,33 +52,92 @@ type Config struct { URL string `json:"url,omitempty" doc:"URL of the API gateway. The value can be the complete URL or an alias. The valid aliases are 'production', 'staging' and 'integration'."` User string `json:"user,omitempty" doc:"User name."` Pager string `json:"pager,omitempty" doc:"Pager command, for example 'less'. If empty no pager will be used."` - AuthFlow string `json:"auth,omitempty" doc:"Authentication method to use. Valid values are 'token', 'client', 'auth-code' and 'device-code'"` + AuthMethod string `json:"auth_method,omitempty" doc:"Authentication method used to login. Valid values are 'token', 'client-credentials', 'auth-code' and 'device-code'"` } -type AuthFlowTypes string +type AuthMethodType string const ( - ClientAuth AuthFlowTypes = "client" - TokenAuth AuthFlowTypes = "token" - AuthCode AuthFlowTypes = "auth-code" - DeviceCode AuthFlowTypes = "device-code" + ClientAuth AuthMethodType = "client-credentials" + TokenAuth AuthMethodType = "token" + AuthCode AuthMethodType = "auth-code" + DeviceCode AuthMethodType = "device-code" ) +// Loads the configuration conditionally based on the auth method +// Should be used for login operations only. +func LoginLoad(authMethod AuthMethodType) (cfg *Config, err error) { + ocmCfg := os.Getenv("OCM_CONFIG") + useSecureStore := ocmCfg == securestore.SecureStoreConfigKey || + authMethod == AuthCode || authMethod == DeviceCode + + // If the OCM_CONFIG env var is set to `securestore`, + // or auth code/device code flow is being used, + // use the OS keyring instead + if useSecureStore { + loadedCfg, err := loadFromOS() + if err != nil { + return nil, err + } + return loadedCfg, nil + } + + // Remove any stored config from OS keyring + // Swallow error if the OS keyring errors + securestore.RemoveConfigFromKeyring() + // No OS keyring config exists, load from file + return loadFromFile() +} + +// Loads the configuration from the OS keyring first, then attempt to load from the configuration file. +// Should be used for operations other than login. +func Load() (cfg *Config, err error) { + loadedCfg, _ := loadFromOS() + // Swallow error if the OS keyring is not available + if loadedCfg != nil { + return loadedCfg, nil + } + + // No OS keyring config exists, load from file + return loadFromFile() +} + +// Loads the configuration from the OS keyring. If the configuration file doesn't exist +// it will return an empty configuration object. +func loadFromOS() (cfg *Config, err error) { + cfg = &Config{} + + data, err := securestore.GetConfigFromKeyring() + if err != nil { + if err == securestore.ErrNoBackendsAvailable { + return nil, fmt.Errorf("an OS keyring is required for authentication: %v", err) + } + return nil, fmt.Errorf("can't load config from OS keyring: %v", err) + } + // No config found, return + if len(data) == 0 { + return nil, nil + } + err = json.Unmarshal(data, cfg) + if err != nil { + // Clear the config from the keyring if it's invalid + // so a new one can be created + err := securestore.RemoveConfigFromKeyring() + if err != nil { + return nil, fmt.Errorf("invalid configuration, automatic recovery failed: %v", err) + } + } + return cfg, nil +} + // Loads the configuration from the configuration file. If the configuration file doesn't exist // it will return an empty configuration object. -func Load() (cfg *Config, err error) { +func loadFromFile() (cfg *Config, err error) { file, err := Location() if err != nil { return } - // If the OCM_CONFIG env var is set to `securestore`, - // or auth code/device code flow is being used, - // load from the OS keyring instead - ocmCfg := os.Getenv("OCM_CONFIG") - if ocmCfg == securestore.SecureStoreConfigKey || ocmCfg == string(AuthCode) || ocmCfg == string(DeviceCode) { - return LoadFromOS() - } _, err = os.Stat(file) if os.IsNotExist(err) { cfg = &Config{} @@ -107,28 +166,6 @@ func Load() (cfg *Config, err error) { return } -// Loads the configuration from the OS keyring. If the configuration file doesn't exist -// it will return an empty configuration object. -func LoadFromOS() (cfg *Config, err error) { - data, err := securestore.GetConfigFromKeyring() - if err != nil || len(data) == 0 { - return cfg, err - } - - cfg = &Config{} - if len(data) == 0 { - return cfg, nil - } - err = json.Unmarshal(data, cfg) - if err != nil { - // Clear the config from the keyring if it's invalid - // treat it as if it doesn't exist so a new one can be created - securestore.UpsertConfigToKeyring([]byte{}) - return cfg, nil - } - return cfg, nil -} - // Save saves the given configuration to the configuration file. func Save(cfg *Config) error { file, err := Location() @@ -141,12 +178,22 @@ func Save(cfg *Config) error { return fmt.Errorf("can't marshal config: %v", err) } + ocmCfg := os.Getenv("OCM_CONFIG") + useSecureStore := ocmCfg == securestore.SecureStoreConfigKey || + cfg.AuthMethod == string(AuthCode) || cfg.AuthMethod == string(DeviceCode) + // If the OCM_CONFIG env var is set to `securestore`, // or auth code/device code flow is being used, - // save to the OS keyring instead - ocmCfg := os.Getenv("OCM_CONFIG") - if ocmCfg == securestore.SecureStoreConfigKey || ocmCfg == string(AuthCode) || ocmCfg == string(DeviceCode) { - return securestore.UpsertConfigToKeyring(data) + // use the OS keyring instead + if useSecureStore { + err := securestore.UpsertConfigToKeyring(data) + if err != nil { + if err == securestore.ErrNoBackendsAvailable { + return fmt.Errorf("an OS keyring is required for authentication: %v", err) + } + return fmt.Errorf("can't save config to OS keyring: %v", err) + } + return nil } dir := filepath.Dir(file) From d7fce9a3fbfb8b075ede2b8977244cf743a6d1ac Mon Sep 17 00:00:00 2001 From: Tyler Creller Date: Tue, 6 Feb 2024 15:36:58 -0500 Subject: [PATCH 6/9] Temp: modify pub/release for testing --- .github/workflows/publish-release.yaml | 118 ++++++++++++------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/.github/workflows/publish-release.yaml b/.github/workflows/publish-release.yaml index 6c29b281..b973df2a 100644 --- a/.github/workflows/publish-release.yaml +++ b/.github/workflows/publish-release.yaml @@ -93,62 +93,62 @@ jobs: build("linux", "s390x") build("windows", "amd64") - # Calculate the SHA256 digests: - for asset in os.listdir(assets): - digest = os.path.join(assets, f"{asset}.sha256") - with open(digest, "wb") as stream: - args = ["sha256sum", asset] - subprocess.run(check=True, cwd=assets, stdout=stream, args=args) - - # Get the list of changes: - body = "" - with open("CHANGES.md", "r") as stream: - while True: - line = stream.readline() - if line == "" or line.startswith("## " + version): - break - while True: - line = stream.readline() - if line == "" or line.startswith("## "): - break - body += line - - # Send the request to create the release: - response = requests.post( - headers={ - "Authorization": f"Bearer {token}", - "Content-Type": "application/json", - "Accept": "application/json", - }, - json={ - "tag_name": f"v{version}", - "name": f"Release {version}", - "body": body, - }, - url=( - "https://api.github.com" - f"/repos/{repository}/releases" - ), - ) - response.raise_for_status() - - # Get the release identifier: - release = response.json()["id"] - - # Upload the assets: - for asset in os.listdir(assets): - file = os.path.join(assets, asset) - with open(file, "rb") as stream: - response = requests.post( - headers={ - "Authorization": f"Bearer {token}", - "Content-Type": "application/octet-stream", - "Accept": "application/json", - }, - data=stream, - url=( - "https://uploads.github.com" - f"/repos/{repository}/releases/{release}/assets?name={asset}" - ), - ) - response.raise_for_status() + # # Calculate the SHA256 digests: + # for asset in os.listdir(assets): + # digest = os.path.join(assets, f"{asset}.sha256") + # with open(digest, "wb") as stream: + # args = ["sha256sum", asset] + # subprocess.run(check=True, cwd=assets, stdout=stream, args=args) + + # # Get the list of changes: + # body = "" + # with open("CHANGES.md", "r") as stream: + # while True: + # line = stream.readline() + # if line == "" or line.startswith("## " + version): + # break + # while True: + # line = stream.readline() + # if line == "" or line.startswith("## "): + # break + # body += line + + # # Send the request to create the release: + # response = requests.post( + # headers={ + # "Authorization": f"Bearer {token}", + # "Content-Type": "application/json", + # "Accept": "application/json", + # }, + # json={ + # "tag_name": f"v{version}", + # "name": f"Release {version}", + # "body": body, + # }, + # url=( + # "https://api.github.com" + # f"/repos/{repository}/releases" + # ), + # ) + # response.raise_for_status() + + # # Get the release identifier: + # release = response.json()["id"] + + # # Upload the assets: + # for asset in os.listdir(assets): + # file = os.path.join(assets, asset) + # with open(file, "rb") as stream: + # response = requests.post( + # headers={ + # "Authorization": f"Bearer {token}", + # "Content-Type": "application/octet-stream", + # "Accept": "application/json", + # }, + # data=stream, + # url=( + # "https://uploads.github.com" + # f"/repos/{repository}/releases/{release}/assets?name={asset}" + # ), + # ) + # response.raise_for_status() From 0069822df741d59c2e631d8e3f866ad2fdf2ba57 Mon Sep 17 00:00:00 2001 From: Tyler Creller Date: Tue, 6 Feb 2024 15:47:16 -0500 Subject: [PATCH 7/9] Add auth method password --- cmd/ocm/login/cmd.go | 2 ++ pkg/config/config.go | 1 + tests/login_test.go | 6 ++++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd/ocm/login/cmd.go b/cmd/ocm/login/cmd.go index 1d1bdd28..dc5852dc 100644 --- a/cmd/ocm/login/cmd.go +++ b/cmd/ocm/login/cmd.go @@ -254,6 +254,8 @@ func run(cmd *cobra.Command, argv []string) error { authMethod = config.DeviceCode } else if haveClientCreds { authMethod = config.ClientAuth + } else if havePassword { + authMethod = config.Password } else { authMethod = config.TokenAuth } diff --git a/pkg/config/config.go b/pkg/config/config.go index abe510b0..47388cbe 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -62,6 +62,7 @@ const ( TokenAuth AuthMethodType = "token" AuthCode AuthMethodType = "auth-code" DeviceCode AuthMethodType = "device-code" + Password AuthMethodType = "password" ) // Loads the configuration conditionally based on the auth method diff --git a/tests/login_test.go b/tests/login_test.go index 9b8f099e..a5a6eb2b 100644 --- a/tests/login_test.go +++ b/tests/login_test.go @@ -75,7 +75,8 @@ var _ = Describe("Login", func() { "{{ $scope }}" {{ end }} ], - "access_token": "{{ .accessToken }}" + "access_token": "{{ .accessToken }}", + "auth_method": "token" }`, "url", sdk.DefaultURL, "tokenURL", ssoServer.URL(), @@ -122,7 +123,8 @@ var _ = Describe("Login", func() { "{{ $scope }}" {{ end }} ], - "access_token": "{{ .accessToken }}" + "access_token": "{{ .accessToken }}", + "auth_method": "client-credentials" }`, "url", sdk.DefaultURL, "tokenURL", ssoServer.URL(), From 9df8a315577c39a3cd84cd6b073e940edda52d29 Mon Sep 17 00:00:00 2001 From: Tyler Creller Date: Tue, 6 Feb 2024 15:50:36 -0500 Subject: [PATCH 8/9] Missed auth method test --- tests/login_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/login_test.go b/tests/login_test.go index a5a6eb2b..0361d2d9 100644 --- a/tests/login_test.go +++ b/tests/login_test.go @@ -173,7 +173,8 @@ var _ = Describe("Login", func() { {{ end }} ], "access_token": "{{ .accessToken }}", - "refresh_token": "{{ .refreshToken }}" + "refresh_token": "{{ .refreshToken }}", + "auth_method": "password" }`, "url", sdk.DefaultURL, "tokenURL", ssoServer.URL(), From d27021131ee212125b63b2f3796c7c38df696733 Mon Sep 17 00:00:00 2001 From: Tyler Creller Date: Tue, 6 Feb 2024 16:08:55 -0500 Subject: [PATCH 9/9] Temp: Publish release --- .github/workflows/publish-release.yaml | 130 ++++++++++++------------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/.github/workflows/publish-release.yaml b/.github/workflows/publish-release.yaml index b973df2a..c90406f3 100644 --- a/.github/workflows/publish-release.yaml +++ b/.github/workflows/publish-release.yaml @@ -87,68 +87,68 @@ jobs: # Build for the supported operating systems and architectures: build("darwin", "amd64") build("darwin", "arm64") - build("linux", "amd64") - build("linux", "arm64") - build("linux", "ppc64le") - build("linux", "s390x") - build("windows", "amd64") - - # # Calculate the SHA256 digests: - # for asset in os.listdir(assets): - # digest = os.path.join(assets, f"{asset}.sha256") - # with open(digest, "wb") as stream: - # args = ["sha256sum", asset] - # subprocess.run(check=True, cwd=assets, stdout=stream, args=args) - - # # Get the list of changes: - # body = "" - # with open("CHANGES.md", "r") as stream: - # while True: - # line = stream.readline() - # if line == "" or line.startswith("## " + version): - # break - # while True: - # line = stream.readline() - # if line == "" or line.startswith("## "): - # break - # body += line - - # # Send the request to create the release: - # response = requests.post( - # headers={ - # "Authorization": f"Bearer {token}", - # "Content-Type": "application/json", - # "Accept": "application/json", - # }, - # json={ - # "tag_name": f"v{version}", - # "name": f"Release {version}", - # "body": body, - # }, - # url=( - # "https://api.github.com" - # f"/repos/{repository}/releases" - # ), - # ) - # response.raise_for_status() - - # # Get the release identifier: - # release = response.json()["id"] - - # # Upload the assets: - # for asset in os.listdir(assets): - # file = os.path.join(assets, asset) - # with open(file, "rb") as stream: - # response = requests.post( - # headers={ - # "Authorization": f"Bearer {token}", - # "Content-Type": "application/octet-stream", - # "Accept": "application/json", - # }, - # data=stream, - # url=( - # "https://uploads.github.com" - # f"/repos/{repository}/releases/{release}/assets?name={asset}" - # ), - # ) - # response.raise_for_status() + # build("linux", "amd64") + # build("linux", "arm64") + # build("linux", "ppc64le") + # build("linux", "s390x") + # build("windows", "amd64") + + # Calculate the SHA256 digests: + for asset in os.listdir(assets): + digest = os.path.join(assets, f"{asset}.sha256") + with open(digest, "wb") as stream: + args = ["sha256sum", asset] + subprocess.run(check=True, cwd=assets, stdout=stream, args=args) + + # Get the list of changes: + body = "" + with open("CHANGES.md", "r") as stream: + while True: + line = stream.readline() + if line == "" or line.startswith("## " + version): + break + while True: + line = stream.readline() + if line == "" or line.startswith("## "): + break + body += line + + # Send the request to create the release: + response = requests.post( + headers={ + "Authorization": f"Bearer {token}", + "Content-Type": "application/json", + "Accept": "application/json", + }, + json={ + "tag_name": f"v{version}", + "name": f"Release {version}", + "body": body, + }, + url=( + "https://api.github.com" + f"/repos/{repository}/releases" + ), + ) + response.raise_for_status() + + # Get the release identifier: + release = response.json()["id"] + + # Upload the assets: + for asset in os.listdir(assets): + file = os.path.join(assets, asset) + with open(file, "rb") as stream: + response = requests.post( + headers={ + "Authorization": f"Bearer {token}", + "Content-Type": "application/octet-stream", + "Accept": "application/json", + }, + data=stream, + url=( + "https://uploads.github.com" + f"/repos/{repository}/releases/{release}/assets?name={asset}" + ), + ) + response.raise_for_status()