-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
1 parent
d91b335
commit 8d67d10
Showing
4 changed files
with
258 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
package bitwarden | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"os" | ||
"slices" | ||
"strings" | ||
|
||
"gopkg.in/yaml.v2" | ||
|
||
"github.com/helmfile/vals/pkg/api" | ||
"github.com/helmfile/vals/pkg/log" | ||
) | ||
|
||
type bwData struct { | ||
Object string `json:"object"` | ||
Data string `json:"data"` | ||
} | ||
|
||
type bwResponse struct { | ||
Success bool `json:"success"` | ||
Message string `json:"message"` | ||
Data bwData `json:"data"` | ||
} | ||
|
||
type provider struct { | ||
log *log.Logger | ||
|
||
Address string | ||
SSLVerify bool | ||
} | ||
|
||
func New(l *log.Logger, cfg api.StaticConfig) *provider { | ||
p := &provider{ | ||
log: l, | ||
Address: getAddressConfig(cfg.String("address")), | ||
} | ||
|
||
return p | ||
} | ||
|
||
func (p *provider) GetString(key string) (string, error) { | ||
itemId, keyType, err := extractItemAndType(key) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
url := fmt.Sprintf("%s/object/%s/%s", | ||
p.Address, | ||
keyType, | ||
itemId) | ||
|
||
client := &http.Client{} | ||
req, err := http.NewRequest(http.MethodGet, url, nil) | ||
if err != nil { | ||
return "", err | ||
} | ||
req.Header = http.Header{ | ||
"Content-Type": {"application/json"}, | ||
} | ||
|
||
res, err := client.Do(req) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
defer func() { | ||
_ = res.Body.Close() | ||
}() | ||
|
||
var resp bwResponse | ||
err = json.NewDecoder(res.Body).Decode(&resp) | ||
if err != nil { | ||
return "", fmt.Errorf("bitwarden: get string key %q, cannot decode JSON: %v", key, err) | ||
} | ||
|
||
if !resp.Success { | ||
return "", fmt.Errorf("bitwarden: get string key %q, msg: %s", key, resp.Message) | ||
} | ||
|
||
return resp.Data.Data, nil | ||
} | ||
|
||
func (p *provider) GetStringMap(key string) (map[string]interface{}, error) { | ||
secretMap := map[string]interface{}{} | ||
|
||
secretString, err := p.GetString(key) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if err := yaml.Unmarshal([]byte(secretString), secretMap); err != nil { | ||
return nil, fmt.Errorf("failed to unmarshal secret: %w", err) | ||
} | ||
|
||
return secretMap, nil | ||
} | ||
|
||
func getAddressConfig(cfgAddress string) string { | ||
if cfgAddress != "" { | ||
return cfgAddress | ||
} | ||
|
||
envAddr := os.Getenv("BW_API_ADDR") | ||
if envAddr != "" { | ||
return envAddr | ||
} | ||
|
||
return "http://localhost:8087" | ||
} | ||
|
||
func extractItemAndType(key string) (string, string, error) { | ||
keyType := "password" | ||
|
||
if len(key) == 0 { | ||
return "", "", fmt.Errorf("bitwarden: key cannot be empty") | ||
} | ||
|
||
splits := strings.Split(key, "/") | ||
itemId := splits[0] | ||
|
||
if len(itemId) == 0 { | ||
return "", "", fmt.Errorf("bitwarden: key cannot be empty") | ||
} | ||
|
||
if len(splits) > 1 { | ||
keyType = splits[1] | ||
} | ||
|
||
if !slices.Contains([]string{"username", "password", "uri", "notes", "item"}, keyType) { | ||
return "", "", fmt.Errorf("bitwarden: get string: key %q unknown keytype %q", key, keyType) | ||
} | ||
|
||
return itemId, keyType, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package bitwarden | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
) | ||
|
||
func TestGetAddressConfig(t *testing.T) { | ||
// Test case 1: cfgAddress is not empty | ||
cfgAddress := "http://example.com" | ||
expected := "http://example.com" | ||
result := getAddressConfig(cfgAddress) | ||
if result != expected { | ||
t.Errorf("Expected %s, but got %s", expected, result) | ||
} | ||
|
||
// Test case 2: cfgAddress is empty, but envAddr is not empty | ||
os.Setenv("BW_API_ADDR", "http://env.example.com") | ||
expected = "http://env.example.com" | ||
result = getAddressConfig("") | ||
os.Unsetenv("BW_API_ADDR") | ||
if result != expected { | ||
t.Errorf("Expected %s, but got %s", expected, result) | ||
} | ||
|
||
// Test case 3: cfgAddress and envAddr are empty | ||
os.Setenv("BW_API_ADDR", "") | ||
expected = "http://localhost:8087" | ||
result = getAddressConfig("") | ||
os.Unsetenv("BW_API_ADDR") | ||
if result != expected { | ||
t.Errorf("Expected %s, but got %s", expected, result) | ||
} | ||
} | ||
|
||
func TestExtractItemAndType(t *testing.T) { | ||
testCases := []struct { | ||
key string | ||
expectedItemId string | ||
expectedKeyType string | ||
expectedErrorMsg string | ||
}{ | ||
{ | ||
key: "item012", | ||
expectedItemId: "item012", | ||
expectedKeyType: "password", | ||
expectedErrorMsg: "", | ||
}, | ||
{ | ||
key: "item123/password", | ||
expectedItemId: "item123", | ||
expectedKeyType: "password", | ||
expectedErrorMsg: "", | ||
}, | ||
{ | ||
key: "item456/username", | ||
expectedItemId: "item456", | ||
expectedKeyType: "username", | ||
expectedErrorMsg: "", | ||
}, | ||
{ | ||
key: "item789/invalid", | ||
expectedItemId: "", | ||
expectedKeyType: "", | ||
expectedErrorMsg: "bitwarden: get string: key \"item789/invalid\" unknown keytype \"invalid\"", | ||
}, | ||
{ | ||
key: "", | ||
expectedItemId: "", | ||
expectedKeyType: "", | ||
expectedErrorMsg: "bitwarden: key cannot be empty", | ||
}, | ||
{ | ||
key: "/password", | ||
expectedItemId: "", | ||
expectedKeyType: "", | ||
expectedErrorMsg: "bitwarden: key cannot be empty", | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
itemId, keyType, err := extractItemAndType(tc.key) | ||
if err != nil && err.Error() != tc.expectedErrorMsg { | ||
t.Errorf("Expected error message %q, but got %q", tc.expectedErrorMsg, err.Error()) | ||
} | ||
if itemId != tc.expectedItemId { | ||
t.Errorf("Expected itemId %q, but got %q", tc.expectedItemId, itemId) | ||
} | ||
if keyType != tc.expectedKeyType { | ||
t.Errorf("Expected keyType %q, but got %q", tc.expectedKeyType, keyType) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters