GithubHelp home page GithubHelp logo

1password / connect-sdk-go Goto Github PK

View Code? Open in Web Editor NEW
155.0 13.0 18.0 256 KB

Go SDK for 1Password Connect

Home Page: https://developer.1password.com/docs/connect

License: MIT License

Go 93.83% Makefile 2.30% Shell 3.87%
1password 1password-connect go golang secrets-management connect-sdk

connect-sdk-go's Introduction

1Password Connect SDK for Go

Access your 1Password items in your Go applications through your self-hosted 1Password Connect server.

Get started

The 1Password Connect Go SDK provides access to the 1Password Connect API, to facilitate communication with the Connect server hosted on your infrastructure and 1Password. The library is intended to be used by your applications, pipelines, and other automations to simplify accessing items stored in your 1Password vaults.

โœจ Quickstart

  1. Download and install the 1Password Connect Go SDK:

    go get github.com/1Password/connect-sdk-go
  2. Export the OP_CONNECT_HOST and OP_CONNECT_TOKEN environment variables:

    export OP_CONNECT_HOST=<your-connect-host> && \
        export OP_CONNECT_TOKEN=<your-connect-token>
  3. Use it in your code

    • Read a secret:

      import "github.com/1Password/connect-sdk-go/connect"
      
      func main () {
          client := connect.NewClientFromEnvironment()
          item, err := client.GetItem("<item-uuid>", "<vault-uuid>")
          if err != nil {
              log.Fatal(err)
          }
      }
    • Write a secret:

      import (
          "github.com/1Password/connect-sdk-go/connect"
          "github.com/1Password/connect-sdk-go/onepassword"
      )
      
      func main () {
          client := connect.NewClientFromEnvironment()
          item := &onepassword.Item{
              Title:    "Secret String",
              Category: onepassword.Login,
              Fields: []*onepassword.ItemField{{
                  Value: "mysecret",
                  Type:  "STRING",
              }},
          }
      
          postedItem, err := client.CreateItem(item, "<vault-uuid>")
          if err != nil {
              log.Fatal(err)
          }
      }

For more examples, check out USAGE.md.

๐Ÿ’™ Community & Support

๐Ÿ” Security

1Password requests you practice responsible disclosure if you discover a vulnerability.

Please file requests via BugCrowd.

For information about security practices, please visit the 1Password Bug Bounty Program.

connect-sdk-go's People

Contributors

accraw avatar briansmith avatar crhntr avatar dckcode avatar edif2008 avatar florisvdg avatar hculea avatar jillianwilson avatar jkyjustin avatar jpcoenen avatar kpcraig avatar marton6 avatar obay avatar rohannagar avatar roopakv avatar snarlysodboxer avatar thatguygriff avatar verkaufer avatar volodymyrzotov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

connect-sdk-go's Issues

Report error message to user of SDK

Summary

Instead of only returning the status code to the user of the SDK, it would be helpful if the SDK also returned the error message returned by Connect, if available.

Use cases

This makes it way easier to debug problems, especially validations issues.

If you currently try to create a new Item, but it does not adhere to the required structure, you only get this response:

Unable to create item. Receieved "400 Bad Request" for "/v1/vaults/UUID/items "

Proposed solution

I propose two possible solutions:

  1. Append the error message to the current string-based error message. I.e. Unable to create item. Receieved "400 Bad Request" for "/v1/vaults/UUID/items": Validation: (validateVaultItem failed to Validate), Couldn't validate the item: "[ItemValidator] has found 1 errors, 0 warnings: \nErrors:{1. details.sections[0].fields[0] has unsupported field type}"
  2. Create a custom error type that exposes the status code and the message to the user of the SDK.

The second option is a little more involved, but I think it's the most flexible to work with and is my preferred option.

Have a list of constants for supported character sets for password generation recipe

Summary

Currently, when making a password recipe, you need to specify the character sets that can be used when generating the password (e.g. SYMBOLS, LETTERS, DIGITS). However, it can be tricky to know by heart the supported character sets.

Also, it would be nice to have a sort of validation of the recipe before sending the request to Connect. As a starting inspiration, you can check the validRecipe function from Node SDK.

Tasks to be done

  • Add a new type that will be used as a field purpose, along with the constants that represent the supported character sets. Add this in onepassword/items/go. Please ensure that all constants are set by checking out this part of the Connect API documentation
    Note: Feel free to change the name of the type, as well as the constant names. This is just a sample code of what it can look like.
    type CharacterSets string
    
    const (
        Letters CharacterSets = "LETTERS"
        Symbols CharacterSets = "SYMBOLS"
        Digits  CharacterSets = "DIGITS"
    )
  • Change the CharacterSets property of the GeneratorRecipe struct to use the new type. (line 132)
    // GeneratorRecipe Representation of a "recipe" used to generate a field
    type GeneratorRecipe struct {
        Length            int             `json:"length,omitempty"`
        CharacterSets     []CharacterSets `json:"characterSets,omitempty"`
        ExcludeCharacters string          `json:"excludeCharacters,omitempty"`
    }
  • If needed, add a validator function that validates the provided list of character sets. If this is added, also add tests that make sure the validator works as expected.

Cannot Create Document type Items because File.content is not exported

Your environment

SDK Version: 1.2.0
Connect Server Version: 1.5.0
OS: MacOS
Go Version: 1.17.2

What happened?

File.content is not exported, so when calling Client.CreateItem(item) on an item with Files, the file's contents are left out by json.Marshal(item).

Here's how how I'm calling CreateItem

	// TODO generate random IDs?
	vaultID := "fdsasdkfjsdkfjhsd" // retrieved first
	itemName := "my-item"
	itemID := fmt.Sprintf("%s-id", itemName)
	fileName := "my-file.txt"
	fileID := fmt.Sprintf("%s-id", fileName)
	contents, err := os.ReadFile(fileName)
	if err != nil {
		return err
	}

	contentPath := fmt.Sprintf("v1/vaults/%s/items/%s/files/%s/content", vaultID, itemID, fileID)
	file := &onepassword.File{
		ID:          fileID,
		Name:        fileName,
		Size:        len(contents),
		ContentPath: contentPath,
	}
	file.SetContent(contents)

	item := &onepassword.Item{
		ID:       itemID,
		Title:    itemName,
		Vault:    onepassword.ItemVault{ID: vaultID},
		Category: onepassword.Document,
		Files:    []*onepassword.File{file},
	}

	_, err = client.CreateItem(item, vaultID)
	if err != nil {
		return err
	}

Connect API - failed to GetFileWithUUID

Your environment

SDK Version: v1.2.0
Connect Server Version: 1.5.0
OS: Ubuntu 20.04.2 LTS
Go Version: go1.17.2 linux/amd64

What happened?

I was trying to download all the documents of a particular vault using the sdk, I noticed that a few files were not downloaded and when I checked the docker logs for the connect API service I saw the error

log_message":"(W) Server: (failed to GetFileWithUUID), attempting to decrypt message with KID using KID , client used wrong Key","timestamp":"2022-02-21T07:23:18.570941261Z

I'm using the method client.GetFileContent() to get the file content

I also saw a old post for the cli : https://1password.community/discussion/87002/op-get-document-fails-with-a-500-error , I think it might be the same issue, I re-uploaded the file and the issue was solved but Is there a way I can gracefully handle this in code without the need to re-upload the file?

What did you expect to happen?

To get the file content without any issue

Steps to reproduce

this only happens for a few files

Create new release

Hello,

I'm in the process of integrating 1Password for secret automation in go using this SDK and saw that what I consider one of the best features described in the documentation "LoadStruct" is not yet available through a release. For now I'm referencing this package by a commit hash, but it would be nice if you could create a new release of the sdk.

PS. Keep up the good work, really cool package.

Thanks in advance,
Daniel Fernandes

Support for custom http client

Summary

Instead of always defaulting to http.DefaultClient, allowing passing one during NewClient configuration

Use cases

This would allow one to specify a custom roundtripper without mutating the http.DefaultClient global variable, this could be useful for adding certificates for custom endpoints that use self-signed certificates or are missing intermediaries.

Proposed solution

Simply allow passing an http client in the New* constructors

Is there a workaround to accomplish this today?

Mutate the global http.DefaultClient which is bad practice and has unintended side-effects for shared code

References & Prior Work

Nope

CloudQuery 1Password Plugin

Hi Team, hopefully this is right place to ask, if not, I'd appreciate if you can direct me.

I'm the founder of cloudquery.io, a high performance open source ELT framework.

Our users are interested in a 1Password plugin, but as we cannot maintain all the plugins ourselves, I was curious if this would be an interesting collaboration, where we would help implement an initial source plugin, and you will help maintain it.

This will give your users the ability to sync 1Password APIs to any of their datalakes/data-warehouses/databases easily using any of the growing list of CQ destination plugins.

As a sidenote: this can also simplify/replace the following just by maintaing one plugin:

Best,
Yevgeny

Error on attempt to get sdk

Your environment

SDK Version:
https://github.com/1Password/connect-sdk-go/tree/v1.0.1

Connect Server Version:
Any

OS:
Any

Go Version:
Any

What happened?

When I click on the "copy" button next to the code at https://github.com/1Password/connect-sdk-go/tree/v1.0.1#installation and paste it into my terminal the next error occurs:

zsh: command not found: $

What did you expect to happen?

Package should be downloaded without issues.

Steps to reproduce

Open https://github.com/1Password/connect-sdk-go/tree/v1.0.1#installation and click on "copy code" button next to the code example.

Notes & Logs

The $ sign should be removed from the code example to fix the issue.

Trim newlines of token

Summary

A token with additional newlines is not considered valid and results in a rather unhelpful error message: Failed to retrieve item: Get "http://onepassword-connect:8080/v1/vaults?filter=title+eq+%22engineering%22": net/http: invalid header field value for "Authorization". Trimming newlines would resolve the problem.

This is not a bug, because headers shouldn't contain newlines, but trimming the token string would result in a better developer-experience. It's rather easy to create such an invalid token with kubectl create secret generic op-token --from-file=token=token.txt because a lot of text editors are configured to add a trailing newline.

Use cases

Can save a few hours of debugging for anyone that doesn't follow the getting started guide exactly. In my case I integrated it into an existing Kustomize setup. Kustomize emphasises a declarative approach and loading the secret from a file is therefore not a strange approach.

Proposed solution

Please trim the token. I'd open up a PR but I'm not familiar with Go and its toolchain

Is there a workaround to accomplish this today?

Yes, remove newlines with some bash-fu

cat token.txt | tr -d "\n" > trimmed_token.txt

References & Prior Work

n/a

GetItemsByTitle doesn't work as (at least I) expected - Items are not fetched

Your environment

SDK Version: v1.2.0

Connect Server Version: 1.3.1

OS: Mac OS

Go Version: go1.17.2 darwin/amd64

What happened?

If you use GetItemByTitle, you get a pointer to an Item which you can then run item.GetValue(field) against. However if you use GetItemsByTitle (plural) and loop over the results and run item.GetValue(field), you'll get empty results, because you haven't called client.GetItem(items[0].ID, items[0].Vault.ID). This makes it appear like the values don't exist when they actually do.

What did you expect to happen?

I expected to be able to loop over the results of GetItemsByTitle and run item.GetValue(field). - Or some documentation to direct me to first run GetItem on the results of GetItemsByTitle.

Steps to reproduce

Described in What Happened.

Notes & Logs

Happy to add any clarity I missed.

connect.readResponseBody does not properly report HTTP errors without a JSON body

Your environment

SDK Version: 1.3.0

Connect Server Version: Will update once I find out (client issue, probably doesn't matter)

OS: MacOS 12.4

Go Version: 1.18.3

What happened?

There was a HTTP 502 Bad Gateway error on http requests to the machine running the Connect server.
Instead of checking the http response code and returning an appropriate error, I received the following error:

import (
	onePasswordConnect "github.com/1Password/connect-sdk-go/connect"
)

func reproduceError(url string, token string, vault string, item string) {
	onePasswordClient := onePasswordConnect.NewClient(url, token)
	onePasswordItem, err := onePasswordClient.GetItem(item, vault) // <-- returns decoding error
}
coding error response: invalid character '<' looking for beginning of value

goroutine 1 [running]:
/somepackage/onepassword.getFile({PARAMS OMITTED FOR PRIVACY})
...

This is caused by the following function, which expects all HTTP responses with a non-ok code to contain a json body, and returns a decoding error otherwise:

func readResponseBody(resp *http.Response, expectedStatusCode int) ([]byte, error) {

What did you expect to happen?

Unexpected HTTP status codes return a descriptive error. Gracefully handle the http error response body not containing a JSON body.

Steps to reproduce

  1. Make a request to a server (that may or may not be running 1password Connect successfully) that will return a non-ok http response code, but does not contain a JSON response body
  2. Use any function that makes a REST request, e.g. connect.GetItem
  3. Inspect the returned error

Notes & Logs

If you'd like a PR to fix this, I can work on it.

Have a list of constants for Item field types

Summary

Currently when making an Item, you need to specify a type for each field you create. (e.g. STRING, CONCEALED, OTP, EMAIL). However, it can be a bit tricky to know by heart what the right purpose is for each field.
Therefore, we want to add the constants for supported field purposes, according to the API spec.

Tasks to be done

  • Add a new type that will be used as a field purpose, along with the constants that represent the supported field purposes. Add this in onepassword/items.go
    type ItemFieldType string
    
    const (
        FieldTypeAddress          ItemFieldType = "ADDRESS"
        FieldTypeConcealed        ItemFieldType = "CONCEALED"
        FieldTypeCreditCardNumber ItemFieldType = "CREDIT_CARD_NUMBER"
        FieldTypeCreditCardType   ItemFieldType = "CREDIT_CARD_TYPE"
        FieldTypeDate             ItemFieldType = "DATE"
        FieldTypeEmail            ItemFieldType = "EMAIL"
        FieldTypeGender           ItemFieldType = "GENDER"
        FieldTypeMenu             ItemFieldType = "MENU"
        FieldTypeMonthYear        ItemFieldType = "MONTH_YEAR"
        FieldTypeOTP              ItemFieldType = "OTP"
        FieldTypePhone            ItemFieldType = "PHONE"
        FieldTypeReference        ItemFieldType = "REFERENCE"
        FieldTypeString           ItemFieldType = "STRING"
        FieldTypeURL              ItemFieldType = "URL"
        FieldTypeFile             ItemFieldType = "FILE"
        FieldTypeSSHKey           ItemFieldType = "SSH_KEY"
        FieldTypeUnknown          ItemFieldType = "UNKNOWN"
    )
  • Change the Purpose property of the ItemFiled struct to have the new type. (line 108)
    // ItemField Representation of a single field on an Item
    type ItemField struct {
        ID       string           `json:"id"`
        Section  *ItemSection     `json:"section,omitempty"`
        Type     ItemFieldType    `json:"type"`
        Purpose  string           `json:"purpose,omitempty"`
        Label    string           `json:"label,omitempty"`
        Value    string           `json:"value,omitempty"`
        Generate bool             `json:"generate,omitempty"`
        Recipe   *GeneratorRecipe `json:"recipe,omitempty"`
        Entropy  float64          `json:"entropy,omitempty"`
        TOTP     string           `json:"totp,omitempty"`
    }

Want to create Document type Items via the API

Summary

We want to be able to create Document type Items with files in them via the Connect API.

Use cases

With the growth of Automation Environments and, for example, Kubernetes Operators that use them, the need also grows to be able to automate the setup of a Vault with all the right items for an environment. Currently that can only be done for Password type Items, see #45.

Proposed solution

I understand there's currently no backend endpoint to support this, and that it's in your backlog. ๐Ÿ‘ This issue is just to track the need and allow people to give it a thumbs up.

Is there a workaround to accomplish this today?

Yes, we hire someone expensive to click a few hundred times through the desktop app to create Document type Items. ๐Ÿ˜›

References & Prior Work

As mentioned above, see #45, but also search for Scripting (Document type) here: https://external-secrets.io/v0.5.3/provider-1password-automation/

Have a list of constants for Item field purposes

Summary

Currently when making an Item, you need to specify a purpose for a field to make sure it is used in autofill (e.g. username and password fields). However, it can be a bit tricky to know by heart what the right purpose is for each field.
Therefore, we want to add the constants for supported field purposes, according to the API spec.

Tasks to be done

  • Add a new type that will be used as a field purpose, along with the constants that represent the supported field purposes. Add this in onepassword/items.go
    type ItemFieldPurpose string
    
    const (
        FieldPurposeUsername ItemFieldPurpose = "USERNAME"
        FieldPurposePassword ItemFieldPurpose = "PASSWORD"
        FieldPurposeNotes    ItemFieldPurpose = "NOTES"
    )
  • Change the Purpose property of the ItemFiled struct to have the new type. (line 109)
    // ItemField Representation of a single field on an Item
    type ItemField struct {
        ID       string           `json:"id"`
        Section  *ItemSection     `json:"section,omitempty"`
        Type     string           `json:"type"`
        Purpose  ItemFieldPurpose `json:"purpose,omitempty"`
        Label    string           `json:"label,omitempty"`
        Value    string           `json:"value,omitempty"`
        Generate bool             `json:"generate,omitempty"`
        Recipe   *GeneratorRecipe `json:"recipe,omitempty"`
        Entropy  float64          `json:"entropy,omitempty"`
        TOTP     string           `json:"totp,omitempty"`
    }

Struct loading with dynamic vault and item

Summary

A function for loading a Go struct from a tag, where only the opfield tag has to be set.

Use cases

Loading multiple similar configurations from different items while using the struct tags:

type Config struct {
    Database string     `opitem: item_id, opfield:".database"`
    Username string     `opitem: item_id, opfield:".username"`
    Password string     `opitem: item_id, opfield:".password"`
}

func main(){
     // setup client    
 
    var config1 Config // should be loaded from item 1
    var config2 Config // should be loaded from item 2
}

There is currently no way to dynamically set the opitem in this case.

Proposed solution

type Config struct {
    Database string     `opfield:".database"`
    Username string     `opfield:".username"`
    Password string     `opfield:".password"`
}

func main(){
     // setup client    

     connect.LoadFromItem(client, &config1, "vault uuid", "item 1 uuid")
     connect.LoadFromItem(client, &config2, "vault uuid", "item 2 uuid")    
}

Some considerations here:

  • Should setting the opvault and/or opitem struct tags override the value provided to LoadFromItem?
  • Is there a more descriptive name than LoadFromItem?
  • Do we want the vault UUID to be an argument to this function? (it's not from Load())

Is there a workaround to accomplish this today?

Without struct tags, it's possible to a struct from multiple items. By fetching the item with, for example, client.GetItemByTitle() and setting the fields manually:

  item, err := client.GetItemByTitle(itemName, vaultUUID)
  if err != nil {
       return nil, err
  }
  config := Config{
        Database: item.Fields["database"].Value,
        Username: item.Fields["username"].Value,
        Password: item.Fields["password"].Value,
  }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.