Go SDK (v.0.0.1)

Space and Time Golang SDK (Go version >= 1.18)

Installation Instructions

  1. git clone https://github.com/spaceandtimelabs/SxT-Go-SDK.git
  2. Before running the code, rename the.env.sample to .env and ensure that your credentials are set up in the .env file properly. You will need to obtain a joinCode and endpoint before you can begin. Please our docs for information about getting your joinCode and API endpoint URL.
  3. Install SDK dependencies:
go mod tidy

Features

Sessions

The SDK can implement persistent storage in

  1. File-based sessions
  2. AWS Secrets Manager

The SxT Golang SDK implements API V2 of AWS SDK (https://github.com/aws/aws-sdk-go-v2). Also, access keys and access secrets are retrieved from sharedConfig and sharedCredentials. You can read more here.

Encryption

Support for ED25519 public key encryption for biscuit authorization and securing data in the platform.

SQL support

๐Ÿช„ Support for DDL: Configure Resources - Perform all CREATE, ALTER, DROP commands

๐Ÿงช Support for DML: Modify data - Perform all INSERT, UPDATE, MERGE, DELETE commands

๐Ÿ’ฅ Support for SQL: Execute queries - Perform all SELECT commands

๐Ÿ”Ž Support for SQL Views: Execute a view

Platform discovery

For fetching metadata from the platform:

  • Namespaces
  • Tables
  • Table columns
  • Table index
  • Table primary key
  • Table relations
  • Primary key references
  • Foreign key references
  • List views

Examples

Running all examples at once

main.go contains a complete running example. To run the file:

go run main.go

To pass an existing user, use:

go run main.go -userid=<USERID> -pubkey=<BASE64 STD ENCODED PUBLIC KEY> -privkey=<BASE64 STD ENCODED PRIVATE KEY>

// e.g
// pubkey=SiQrMfU+TfRrqqeo/ZDoOwSrHd9zrG1BCU4oDz+4C4Q=
// privkey=ys3hQPyfojJOzNymc0eWOKUiogQFGv3G+eeEDUBB8jpKJCsx9T5N9Guqp6j9kOg7BKsd33OsbUEJTigPP7gLhA=

// The keys are provided for example and will not work

This will bypass the userid and joincode mentioned in .env file.

:bulb: SxT libraries may generate a 32-byte or a 64-byte base64 encoded private key. A 64-byte key is a combination of 32-byte secret and 32-byte public key.

For details on running the main.go file, us:

go run main.go -h

Authentication

It is very important to save your private key used in authentication and biscuit generation, or else you will not be able to have access to the user and the tables created using the key.

The generated AccessToken is valid for 25 minutes, and the refreshToken for 30 minutes.

// New Authentication.
// Generates new accessToken, refreshToken, privateKey, and publicKey
func authenticate()(accessToken, refreshToken string, privateKey ed25519.PrivateKey, publicKey ed25519.PublicKey){

    // Read userId, joinCode from .env file
	userId, _ := helpers.ReadUserId()
	joinCode, _ := helpers.ReadJoinCode()

	var pubkey ed25519.PublicKey
	var privkey ed25519.PrivateKey

	var authCodeStruct authentication.AuthCodeStruct
	var tokenStruct authentication.TokenStruct

	sessionStruct, sessionStatus := storage.FileReadSession(userId)

	if !sessionStatus {
		// Generate Private and Public keys
		pubkey, privkey = helpers.CreateKey()
	} else {
		pubkey = sessionStruct.PublicKey
		privkey = sessionStruct.PrivateKey
	}


	// Get auth code
	authCode := authentication.GenerateAuthCode(userId, joinCode)
	json.Unmarshal([]byte(authCode), &authCodeStruct)

	// Get Keys
	encodedSignature,  base64PublicKey := authentication.GenerateKeys(authCodeStruct.AuthCode, pubkey, privkey)

	// Get Token
	tokenJson := authentication.GenerateToken(userId, authCodeStruct.AuthCode, encodedSignature, base64PublicKey)
	json.Unmarshal([]byte(tokenJson), &tokenStruct)

	// Store session data to a local file

	writeStatus := storage.FileWriteSession(userId, tokenStruct.AccessToken, tokenStruct.RefreshToken, privkey, pubkey)
	if !writeStatus{
		log.Fatal("Invalid login. Change login credentials")
	}

	return tokenStruct.AccessToken, tokenStruct.RefreshToken, privkey, publicKey
}

Generating biscuits

You can create multiple biscuit tokens for a table, allowing you to provide different access levels for users. For the list of all capabilities, refer to the biscuit authorization documentation.

Sample biscuit generation with permissions for select query, insert query, update query, delete query, create table

var sxtBiscuitCapabilities []authorization.SxTBiscuitStruct

// Add biscuit capabilities
sxtBiscuitCapabilities = append(sxtBiscuitCapabilities, authorization.SxTBiscuitStruct{Operation: "dql_select", Resource: "eth.testtable103"})
sxtBiscuitCapabilities = append(sxtBiscuitCapabilities, authorization.SxTBiscuitStruct{Operation: "dml_insert", Resource: "eth.testtable103"})
sxtBiscuitCapabilities = append(sxtBiscuitCapabilities, authorization.SxTBiscuitStruct{Operation: "dml_update", Resource: "eth.testtable103"})
sxtBiscuitCapabilities = append(sxtBiscuitCapabilities, authorization.SxTBiscuitStruct{Operation: "dml_delete", Resource: "eth.testtable103"})
sxtBiscuitCapabilities = append(sxtBiscuitCapabilities, authorization.SxTBiscuitStruct{Operation: "ddl_create", Resource: "eth.testtable103"})

// Generate the biscuit token
biscuit, _ := authorization.CreateBiscuitToken(sxtBiscuitCapabilities, &privateKey)

For multi biscuit support and setting originApp for identifying logs:

// Create multiple biscuits
mb := []string{biscuit}
originApp := "TEST"

DDL, DML & DQL

Note: To generate a new schema, ddl_create permission is needed.

// Create a new schema
sqlcore.CreateSchema("CREATE SCHEMA ETH")

// Only for create queries
// For ALTER and DROP, use sqlcore.DDL()
sqlcore.CreateTable("CREATE TABLE ETH.TESTTABLE103 (id INT PRIMARY KEY, test VARCHAR)", "permissioned", biscuit, originApp, mb, publicKey)

// Only for ALTER and DROP queries
// For Create table queries, use sqlcore.CreateTable()
sqlcore.DDL("ALTER TABLE ETH.TESTTABLE103 ADD TEST2 VARCHAR", biscuit, originApp, mb )

// DML
// use the sqlcore.DML to write insert, update, delete, and merge queries
sqlcore.DML("ETH.TESTTABLE103", "insert into ETH.TESTTABLE103 values(5, 'x5')", biscuit, originApp, mb);

// DQL
// Select operations
// If rowCount is 0, then fetches all data without limit
sqlcore.DQL("ETH.TESTTABLE103", "select * from ETH.TESTTABLE103", biscuit, originApp, mb, 0);

Discovery

Discovery calls need a user to be logged in.


// List Namespaces
discovery.ListNamespaces()

// List Tables in a given namespace
// Possible scope values -  ALL = all resources, PUBLIC = non-permissioned tables, PRIVATE = tables created by the requesting user
discovery.ListTables("ETH", "ALL")

// List Columns for a given table in namespace
discovery.ListColumns("ETH", "TESTTABLE103")

// List table index for a given table in namespace
discovery.ListTableIndex("ETH", "TESTTABLE103")

// List table primary key for a given table in namespace
discovery.ListTablePrimaryKey("ETH", "TESTTABLE103")

// List table relations for a namespace and scope
// Possible scope values -  ALL = all resources, PUBLIC = non-permissioned tables, PRIVATE = tables created by the requesting user
discovery.ListTableRelations("ETH", "PRIVATE")

// List table primary key references for a table and a namespace
discovery.ListPrimaryKeyReferences("ETH", "TESTTABLE103", "TEST")

// List foreign key references for a table, column and a namespace
discovery.ListForeignKeyReferences("ETH", "TESTTABLE103", "TEST")

Storage

For AWS and file storage, the following methods are available:

// File Storage Methods 
storage.FileWriteSession(userId, tokenStruct.AccessToken, tokenStruct.RefreshToken, privkey, pubkey)

storage.FileReadSession(userId)

storage.FileUpdateSession(userId, accessToken, refreshToken, privateKey, publicKey)

// AWS
storage.AwsWriteSession(userId, tokenStruct.AccessToken, tokenStruct.RefreshToken, privkey, pubkey)

storage.AwsReadSession(userId)

storage.AwsUpdateSession(userId, accessToken, refreshToken, privateKey, publicKey)