Skip to content

Commit

Permalink
OCM-4965: Keyring configuration storage
Browse files Browse the repository at this point in the history
  • Loading branch information
tylercreller committed Feb 19, 2024
1 parent fab7ccf commit 67c6d03
Show file tree
Hide file tree
Showing 12 changed files with 465 additions and 40 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/check-pull-request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,12 @@ jobs:
run: go mod download
- name: Setup Ginkgo
run: go install github.com/onsi/ginkgo/v2/[email protected]
- name: Run the tests
- name: Run the tests (linux, windows)
if: ${{ contains(fromJSON('["ubuntu-latest", "windows-latest"]'), matrix.platform) }}
run: make tests
- name: Run the tests (macOS-only)
if: ${{ contains(fromJSON('["macos-latest"]'), matrix.platform) }}
run: make tests-cgo

golangci:
name: Lint
Expand Down
114 changes: 109 additions & 5 deletions .github/workflows/publish-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ on:
- '*'

jobs:

release:
name: Publish release
release-linux-windows:
name: Publish release (Linux and Windows)
runs-on: ubuntu-latest
steps:
- name: Checkout the source
Expand Down Expand Up @@ -85,8 +84,6 @@ jobs:
os.rename(binary, asset)
# Build for the supported operating systems and architectures:
build("darwin", "amd64")
build("darwin", "arm64")
build("linux", "amd64")
build("linux", "arm64")
build("linux", "ppc64le")
Expand Down Expand Up @@ -152,3 +149,110 @@ jobs:
),
)
response.raise_for_status()
release-macos:
needs: release-linux-windows
name: Publish release (macOS)
runs-on: macos-latest
steps:
- name: Checkout the source
uses: actions/checkout@v3

- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.10'
cache: 'pip'

- name: Install Python modules
run: pip install -r .github/workflows/requirements.txt

- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: 1.18

- name: Create release
shell: python
run: |
import os
import re
import requests
import shutil
import subprocess
# Get the context and secret data that we will need:
repository = "${{ github.repository }}"
reference = "${{ github.ref }}"
token = "${{ secrets.GITHUB_TOKEN }}"
# Calculate the version number:
version = re.sub(r"^refs/tags/v(.*)$", r"\1", reference)
# Make sure that the assets directory exists and is empty:
assets = "assets"
shutil.rmtree(assets, ignore_errors=True)
os.mkdir(assets)
def build(goos: str, goarch: str):
# Set the environment variables that tell the Go compiler which
# operating system and architecture to build for:
env = dict(os.environ)
env["GOOS"] = goos
env["GOARCH"] = goarch
# Build the binary:
args = ["make", "cmds-cgo"]
subprocess.run(check=True, env=env, args=args)
# Copy the generated binary to the assets directory:
binary = "ocm"
asset = os.path.join(assets, f"ocm-{goos}-{goarch}")
os.rename(binary, asset)
# Build for the supported operating systems and architectures:
build("darwin", "amd64")
build("darwin", "arm64")
# 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 = ["shasum", "-a", "256"]
subprocess.run(check=True, cwd=assets, stdout=stream, args=args)
# Get the release:
response = requests.get(
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
},
url=(
"https://api.github.com"
f"/repos/{repository}/releases/tags/v{version}"
),
)
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()
17 changes: 17 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,31 @@ cmds:
go build "./cmd/$${cmd}" || exit 1; \
done

# Used for compiling with CGO_ENABLED=1 (macOS keychain support)
.PHONY: cmds-cgo
cmds-cgo:
for cmd in $$(ls cmd); do \
CGO_ENABLED=1 \
go build "./cmd/$${cmd}" || exit 1; \
done

.PHONY: install
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

.PHONY: test-cgo tests-cgo
test-cgo tests-cgo: cmds-cgo
ginkgo run -r

.PHONY: fmt
fmt:
gofmt -s -l -w cmd pkg tests
Expand Down
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ An `~/config/ocm/ocm.json` file stores login credentials for a single API
server. Using multiple servers therefore requires having to log in and out a lot
or the ability to utilize multiple config files. The latter functionality is
provided with the `OCM_CONFIG` environment variable. If running `ocm login` was
successfull in both cases, the `ocm whoami` commands will return different
successful in both cases, the `ocm whoami` commands will return different
results:

```
Expand All @@ -129,6 +129,39 @@ $ OCM_CONFIG=$HOME/ocm.json.stg ocm whoami

NOTE: Tokens for production and staging will differ.

## Storing Configuration & Tokens in OS Keyring
The `RH_KEYRING` environment variable provides the ability to store the OCM
configuration containing your tokens in your OS keyring. This is provided
as an alternative to storing the configuration in plain-text on your system.
`RH_KEYRING` will override `OCM_CONFIG` if both are set.

`RH_KEYRING` supports the following keyrings:

* [Windows Credential Manager](https://support.microsoft.com/en-us/windows/accessing-credential-manager-1b5c916a-6a16-889f-8581-fc16e8165ac0) - `wincred`
* [macOS Keychain](https://support.apple.com/en-us/guide/keychain-access/welcome/mac) - `keychain`
* Secret Service ([Gnome Keyring](https://wiki.gnome.org/Projects/GnomeKeyring), [KWallet](https://apps.kde.org/kwalletmanager5/), etc.) - `secret-service`
* [Pass](https://www.passwordstore.org/) - `pass`

| | wincred | keychain | secret-service | pass |
| ------------- | ------------- | ------------- | ------------- | ------------- |
| Windows | :heavy_check_mark: | :x: | :x: | :x: |
| macOS | :x: | :heavy_check_mark:* | :x: | :heavy_check_mark: |
| Linux | :x: | :x: | :heavy_check_mark: | :heavy_check_mark: |

<sub>* if building from source CGO_ENABLED=1 is required for macOS keychain support.</sub>

#### See Available Keyrings
The following will list available keyrings in your current context
```
$ ocm config get keyrings
```

#### Remove Keyring Configuration
The following will remove the OCM configuration from your `RH_KEYRING`
```
$ ocm config reset keyring
```

## Obtaining Tokens

If you need the _OpenID_ access token to use it with some other tool, you can
Expand Down
2 changes: 2 additions & 0 deletions cmd/ocm/config/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/spf13/cobra"

"github.com/openshift-online/ocm-cli/cmd/ocm/config/get"
"github.com/openshift-online/ocm-cli/cmd/ocm/config/reset"
"github.com/openshift-online/ocm-cli/cmd/ocm/config/set"
"github.com/openshift-online/ocm-cli/pkg/config"
)
Expand Down Expand Up @@ -73,4 +74,5 @@ var Cmd = &cobra.Command{
func init() {
Cmd.AddCommand(get.Cmd)
Cmd.AddCommand(set.Cmd)
Cmd.AddCommand(reset.Cmd)
}
43 changes: 34 additions & 9 deletions cmd/ocm/config/get/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -48,17 +49,25 @@ func init() {
}

func run(cmd *cobra.Command, argv []string) error {
// Load the configuration file:
cfg, err := config.Load()
if err != nil {
return fmt.Errorf("Can't load config file: %v", err)
// The following variables are not stored in the configuration file
// and can skip loading configuration:
skipConfigLoadMap := map[string]bool{
"keyrings": true,
}

// If the configuration file doesn't exist yet assume that all the configuration settings
// are empty:
if cfg == nil {
fmt.Printf("\n")
return nil
cfg := config.Config{}
if !skipConfigLoadMap[argv[0]] {
// Load the configuration file:
cfg, err := config.Load()
if err != nil {
return fmt.Errorf("Can't load config file: %v", err)
}
// If the configuration file doesn't exist yet assume that all the configuration settings
// are empty:
if cfg == nil {
fmt.Printf("\n")
return nil
}
}

// Print the value of the requested configuration setting:
Expand All @@ -83,9 +92,25 @@ 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":
keyrings, err := getKeyrings()
if err != nil {
return err
}
for _, keyring := range keyrings {
fmt.Fprintf(os.Stdout, "%s\n", keyring)
}
default:
return fmt.Errorf("Unknown setting")
}

return nil
}

func getKeyrings() ([]string, error) {
backends := securestore.AvailableBackends()
if len(backends) == 0 {
return backends, fmt.Errorf("error: no keyrings available")
}
return backends, nil
}
Loading

0 comments on commit 67c6d03

Please sign in to comment.