Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
maksimu committed Sep 23, 2021
1 parent d30a596 commit db0c319
Show file tree
Hide file tree
Showing 30 changed files with 4,833 additions and 4 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/test.go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Test-Go

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
test-go:
strategy:
matrix:
go-version: [1.16.x]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: ./test
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: Run tests
run: go test -p 1 ./...
8 changes: 4 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
*.dll
*.so
*.dylib
*.json

# Test binary, built with `go test -c`
# Test binary, build with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/
.idea/
.DS_Store
21 changes: 21 additions & 0 deletions LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 Keeper Security

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
146 changes: 146 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# Secrets Management Go SDK

![Go](https://github.com/keeper-security/secrets-manager-go/actions/workflows/test.go.yml/badge.svg)

This library provides interface to Keeper® Secrets Manager and can be used to access your Keeper vault, read and update existing records, rotate passwords and more. Keeper Secrets Manager is an open source project with contributions from Keeper's engineering team and partners.

## Features:

## Obtain a One-Time Access Token
Keeper Secrets Manager authenticates your API requests using advanced encryption that uses locally stored private key, device id and client id.
To register your device and generate private key you will need to generate a One-Time Access Token via Web Vault or Keeper Commander CLI.

### Via Web Vault
**Secrets Manager > Applications > Create Application** - will let you chose application name, shared folder(s) and permissions and generate One-Time Access Token. _Note: Keeper does not store One-Time Access Tokens - save or copy the token offline for later use._

One-Time Access Tokens can be generated as needed: **Secrets Manager > Applications > Application Name > Devices Tab > Edit > Add Device button** - will let you create new Device and generate its One-Time Access Token.

[What is an application?](https://docs.keeper.io/secrets-manager/secrets-manager/overview/terminology)

### Via Keeper Commander CLI
Login to Keeper with Commander CLI and perform following:
1. Create Application
```bash
$ sm app create [NAME]
```

2. Share Secrets to the Application
```bash
$ sm share add --app [NAME] --secret [UID] --editable
```
- `--app` - Name of the Application.
- `--secret` - Record UID or Shared Folder UID
- `--editable` - if omitted defaults to false

3. Create client
```bash
$ sm client add --app [NAME] --unlock-ip --count 1
```

### Install
```bash
go get github.com/keeper-security/secrets-manager-go/core
```

### Quick Start

```golang
package main
// Import Secrets Manager
import ksm "github.com/keeper-security/secrets-manager-go/core"
func main() {
// Establish connection
// One time secrets generated via Web Vault or Commander CLI
sm := ksm.NewSecretsManager()
sm.Token = "MmzGdls-rDG39vgqgFD1HL70h0_L_sKQOdI0qwXU3JI"
// One time tokens can be used only once - afterwards use the generated config file
// sm := ksm.NewSecretsManagerFromConfig(ksm.NewFileKeyValueStorage("client-config.json"))
// Retrieve all password records
allRecords, _ := sm.GetSecrets([]string{})
// Get password from first record:
password := allRecords[0].Password()
// WARNING: Avoid logging sensitive data
print("My password from Keeper: ", password)
}
```

## Samples
### File Download
```golang
sm := ksm.NewSecretsManagerFromConfig(ksm.NewFileKeyValueStorage("client-config.json"))
if records, err := sm.GetSecrets([]string{}); err == nil {
for _, r := range records {
fmt.Println("\tTitle: " + r.Title())
for i, f := range r.Files {
fmt.Printf("\t\tfile #%d -> name: %s", i, f.Name)
f.SaveFile("/tmp/"+f.Name, true)
}
}
}
```
### Update record
```golang
sm := ksm.NewSecretsManagerFromConfig(ksm.NewFileKeyValueStorage("client-config.json"))
if records, err := sm.GetSecrets([]string{}); err == nil && len(records) > 0 {
record := records[0]
newPassword := fmt.Sprintf("Test Password - " + time.Now().Format(time.RFC850))
record.SetPassword(newPassword)
record.RawJson = ksm.DictToJson(record.RecordDict)
if err := sm.Save(record); err != nil {
fmt.Println("Error saving record: " + err.Error())
}
}
```
## Configuration
### Types
Listed in priority order
1. Environment variable
1. Configuration store
1. Code
### Available configurations:
- `clientKey` - One Time Access Token used during initialization
- `hostname` - Keeper Backend host. Available values:
- `keepersecurity.com`
- `keepersecurity.eu`
- `keepersecurity.com.au`
- `govcloud.keepersecurity.us`
## Adding more records or shared folders to the Application
### Via Web Vault
Drag&Drop records into the shared folder or select from the record menu any of the options to CreateDuplicate/Move or create new records straight into the shared folder. As an alternative use: Secrets Manager > Application > Application Name > Folders & Records > Edit and use search field to add any folders or records then click Save.
### Via Commander CLI
```bash
sm share add --app [NAME] --secret [UID2]
sm share add --app [NAME] --secret [UID3] --editable
```
### Retrieve secret(s)
```golang
secretsManager := ksm.NewSecretsManagerFromConfig(ksm.NewFileKeyValueStorage("client-config.json"))
allSecrets, _ := secretsManager.GetSecrets([]string{})
```
### Update secret
```golang
secretToUpdate = allSecrets[0]
secretToUpdate.SetPassword("NewPassword123$")
secretsManager.Save(secretToUpdate)
```
70 changes: 70 additions & 0 deletions core/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package core

import (
"os"
"strings"
)

const defautFilePath = "cache.dat"

type ICache interface {
SaveCachedValue(data []byte) error
GetCachedValue() ([]byte, error)
Purge() error
}

// File based cache
type fileCache struct {
FilePath string
}

func (c *fileCache) SaveCachedValue(data []byte) error {
if data == nil {
data = []byte{}
}
return os.WriteFile(c.FilePath, data, 0600)
}

func (c *fileCache) GetCachedValue() ([]byte, error) {
return os.ReadFile(c.FilePath)
}

func (c *fileCache) Purge() error {
return os.Remove(c.FilePath)
}

func NewFileCache(filePath string) *fileCache {
path := strings.TrimSpace(filePath)
if path == "" {
path = defautFilePath
}
return &fileCache{FilePath: path}
}

// Memory based cache
type memoryCache struct {
cache []byte
}

func (c *memoryCache) SaveCachedValue(data []byte) error {
c.cache = []byte{} // always erase old value
if len(data) > 0 {
bytes := make([]byte, len(data))
copy(bytes, data)
c.cache = bytes
}
return nil
}

func (c *memoryCache) GetCachedValue() ([]byte, error) {
return c.cache, nil
}

func (c *memoryCache) Purge() error {
c.cache = []byte{}
return nil
}

func NewMemoryCache() *memoryCache {
return &memoryCache{cache: []byte{}}
}
37 changes: 37 additions & 0 deletions core/config_keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package core

type ConfigKey string

const (
KEY_URL ConfigKey = "url" // base URL for the Secrets Manager service
KEY_SERVER_PUBLIC_KEY_ID ConfigKey = "serverPublicKeyId"
KEY_CLIENT_ID ConfigKey = "clientId"
KEY_CLIENT_KEY ConfigKey = "clientKey" // The key that is used to identify the client before public key
KEY_APP_KEY ConfigKey = "appKey" // The application key with which all secrets are encrypted
KEY_PRIVATE_KEY ConfigKey = "privateKey" // The client's private key
KEY_PUBLIC_KEY ConfigKey = "publicKey" // The client's public key
KEY_HOSTNAME ConfigKey = "hostname" // base hostname for the Secrets Manager service
)

func GetConfigKey(value string) ConfigKey {
switch value {
case string(KEY_URL):
return KEY_URL
case string(KEY_SERVER_PUBLIC_KEY_ID):
return KEY_SERVER_PUBLIC_KEY_ID
case string(KEY_CLIENT_ID):
return KEY_CLIENT_ID
case string(KEY_CLIENT_KEY):
return KEY_CLIENT_KEY
case string(KEY_APP_KEY):
return KEY_APP_KEY
case string(KEY_PRIVATE_KEY):
return KEY_PRIVATE_KEY
case string(KEY_PUBLIC_KEY):
return KEY_PUBLIC_KEY
case string(KEY_HOSTNAME):
return KEY_HOSTNAME
default:
return ""
}
}
Loading

0 comments on commit db0c319

Please sign in to comment.