From cfcd77a70664279162610ba8b6de84b43da78757 Mon Sep 17 00:00:00 2001 From: "Renol P. H." Date: Tue, 16 Apr 2024 19:49:09 +0700 Subject: [PATCH 1/7] add: env --- Makefile | 4 +- core/env/env.go | 1 + core/env/impl/sqliteenv.go | 16 ++++++ core/env/impl/sqliteenv_test.go | 90 +++++++++++++++++++++++++++++++++ core/env/sqldb.go | 3 ++ go.mod | 1 + go.sum | 2 + 7 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 core/env/env.go create mode 100644 core/env/impl/sqliteenv.go create mode 100644 core/env/impl/sqliteenv_test.go create mode 100644 core/env/sqldb.go diff --git a/Makefile b/Makefile index 396de2b..fa54ed2 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,9 @@ TEST = UNIT_TEST_BUILD_DIR = $(BUILD_DIR)/unit-test UNIT_TEST_PKG = \ ./core/crypto \ - ./core/crypto/impl + ./core/crypto/impl \ + ./core/env \ + ./core/env/impl INTEGRATION_TEST_PKG = \ ./core/crypto/test \ diff --git a/core/env/env.go b/core/env/env.go new file mode 100644 index 0000000..13a09ae --- /dev/null +++ b/core/env/env.go @@ -0,0 +1 @@ +package env diff --git a/core/env/impl/sqliteenv.go b/core/env/impl/sqliteenv.go new file mode 100644 index 0000000..1caf4d6 --- /dev/null +++ b/core/env/impl/sqliteenv.go @@ -0,0 +1,16 @@ +package env_impl + +import ( + "github.com/reshifr/secure-env/core/env" +) + +type SQLiteEnv[DB env.SQLDB] struct { + db DB +} + +func LoadSQLiteEnv[DB env.SQLDB](db DB) *SQLiteEnv[DB] { + env := &SQLiteEnv[DB]{db: db} + return env +} + +func (env *SQLiteEnv[DB]) CreateRole() {} diff --git a/core/env/impl/sqliteenv_test.go b/core/env/impl/sqliteenv_test.go new file mode 100644 index 0000000..0e12e7b --- /dev/null +++ b/core/env/impl/sqliteenv_test.go @@ -0,0 +1,90 @@ +package env_impl + +import ( + "database/sql" + "log" + "testing" + + _ "github.com/mattn/go-sqlite3" +) + +// const roleTable = ` +// CREATE TABLE IF NOT EXISTS role( +// id UNSIGNED BIG INT PRIMARY KEY AUTOINCREMENT, +// name VARCHAR(255) UNIQUE COLLATE BINARY, +// ) +// ` + +// CREATE TABLE IF NOT EXISTS secret( +// id UNSIGNED BIG INT PRIMARY KEY AUTOINCREMENT, +// raw_secret BLOB +// ); + +// CREATE TABLE IF NOT EXISTS access( +// role_id UNSIGNED BIG INT, +// secret_id UNSIGNED BIG INT, +// FOREIGN KEY (role_id) REFERENCES role(id), +// FOREIGN KEY (secret_id) REFERENCES secret(id) +// ); + +func CreateTable(db *sql.DB) { + const roleSql = ` + CREATE TABLE IF NOT EXISTS role ( + id INTEGER + PRIMARY KEY + NOT NULL, + name VARCHAR(255) + UNIQUE + NOT NULL + COLLATE BINARY + ) + ` + if _, err := db.Exec(roleSql); err != nil { + log.Fatal(err) + } + + const secretSql = ` + CREATE TABLE IF NOT EXISTS secret ( + id INTEGER + PRIMARY KEY + NOT NULL, + raw_secret BLOB + NOT NULL + ) + ` + if _, err := db.Exec(secretSql); err != nil { + log.Fatal(err) + } + + const accessSql = ` + CREATE TABLE IF NOT EXISTS access ( + role_id INTEGER + NOT NULL, + secret_id INTEGER + NOT NULL, + FOREIGN KEY (role_id) + REFERENCES role(id) + ON DELETE CASCADE + ON UPDATE CASCADE, + FOREIGN KEY (secret_id) + REFERENCES secret(id) + ON DELETE CASCADE + ON UPDATE CASCADE + ) + ` + if _, err := db.Exec(accessSql); err != nil { + log.Fatal(err) + } + + txn, _ := db.Begin() + txn.Exec("INSERT INTO secret(raw_secret) VALUES (?)", []byte{0xff, 0xff}) + txn.Commit() +} + +func Test_SQLiteEnv(t *testing.T) { + db, err := sql.Open("sqlite3", "../../../build/env.db") + if err != nil { + t.Fatal(err) + } + CreateTable(db) +} diff --git a/core/env/sqldb.go b/core/env/sqldb.go new file mode 100644 index 0000000..8d3d9fb --- /dev/null +++ b/core/env/sqldb.go @@ -0,0 +1,3 @@ +package env + +type SQLDB interface{} diff --git a/go.mod b/go.mod index fc6157e..c171e69 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/objx v0.5.2 // indirect golang.org/x/sys v0.19.0 // indirect diff --git a/go.sum b/go.sum index 8aec9b5..c3a8829 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emirpasic/gods/v2 v2.0.0-alpha h1:dwFlh8pBg1VMOXWGipNMRt8v96dKAIvBehtCt6OtunU= github.com/emirpasic/gods/v2 v2.0.0-alpha/go.mod h1:W0y4M2dtBB9U5z3YlghmpuUhiaZT2h6yoeE+C1sCp6A= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= From dd7a78029284b1d0633b38c05ffe2bd937f699d0 Mon Sep 17 00:00:00 2001 From: "Renol P. H." Date: Tue, 16 Apr 2024 20:20:37 +0700 Subject: [PATCH 2/7] fix: remove interface for first development --- core/env/impl/sqliteenv.go | 42 +++++++++++++---- core/env/impl/sqliteenv_test.go | 80 +++------------------------------ core/env/sql.go | 1 + core/env/sqldb.go | 3 -- 4 files changed, 40 insertions(+), 86 deletions(-) create mode 100644 core/env/sql.go delete mode 100644 core/env/sqldb.go diff --git a/core/env/impl/sqliteenv.go b/core/env/impl/sqliteenv.go index 1caf4d6..b8f39a4 100644 --- a/core/env/impl/sqliteenv.go +++ b/core/env/impl/sqliteenv.go @@ -1,16 +1,42 @@ package env_impl import ( - "github.com/reshifr/secure-env/core/env" + "database/sql" ) -type SQLiteEnv[DB env.SQLDB] struct { - db DB +type SQLiteEnv struct { + db *sql.DB } -func LoadSQLiteEnv[DB env.SQLDB](db DB) *SQLiteEnv[DB] { - env := &SQLiteEnv[DB]{db: db} - return env +func LoadSQLiteEnv(db *sql.DB) (*SQLiteEnv, error) { + const role = ` + CREATE TABLE IF NOT EXISTS role( + id INTEGER PRIMARY KEY NOT NULL, + name VARCHAR(255) UNIQUE NOT NULL + )` + if _, err := db.Exec(role); err != nil { + return nil, err + } + const secret = ` + CREATE TABLE IF NOT EXISTS secret( + id INTEGER PRIMARY KEY NOT NULL, + raw BLOB NOT NULL + )` + if _, err := db.Exec(secret); err != nil { + return nil, err + } + const access = ` + CREATE TABLE IF NOT EXISTS access ( + role_id INTEGER NOT NULL, + secret_id INTEGER NOT NULL, + FOREIGN KEY(role_id) REFERENCES role(id) + ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (secret_id) REFERENCES secret(id) + ON DELETE CASCADE ON UPDATE CASCADE + )` + if _, err := db.Exec(access); err != nil { + return nil, err + } + env := &SQLiteEnv{db: db} + return env, nil } - -func (env *SQLiteEnv[DB]) CreateRole() {} diff --git a/core/env/impl/sqliteenv_test.go b/core/env/impl/sqliteenv_test.go index 0e12e7b..2ab30d3 100644 --- a/core/env/impl/sqliteenv_test.go +++ b/core/env/impl/sqliteenv_test.go @@ -2,89 +2,19 @@ package env_impl import ( "database/sql" - "log" "testing" _ "github.com/mattn/go-sqlite3" ) -// const roleTable = ` -// CREATE TABLE IF NOT EXISTS role( -// id UNSIGNED BIG INT PRIMARY KEY AUTOINCREMENT, -// name VARCHAR(255) UNIQUE COLLATE BINARY, -// ) -// ` - -// CREATE TABLE IF NOT EXISTS secret( -// id UNSIGNED BIG INT PRIMARY KEY AUTOINCREMENT, -// raw_secret BLOB -// ); - -// CREATE TABLE IF NOT EXISTS access( -// role_id UNSIGNED BIG INT, -// secret_id UNSIGNED BIG INT, -// FOREIGN KEY (role_id) REFERENCES role(id), -// FOREIGN KEY (secret_id) REFERENCES secret(id) -// ); - -func CreateTable(db *sql.DB) { - const roleSql = ` - CREATE TABLE IF NOT EXISTS role ( - id INTEGER - PRIMARY KEY - NOT NULL, - name VARCHAR(255) - UNIQUE - NOT NULL - COLLATE BINARY - ) - ` - if _, err := db.Exec(roleSql); err != nil { - log.Fatal(err) - } - - const secretSql = ` - CREATE TABLE IF NOT EXISTS secret ( - id INTEGER - PRIMARY KEY - NOT NULL, - raw_secret BLOB - NOT NULL - ) - ` - if _, err := db.Exec(secretSql); err != nil { - log.Fatal(err) - } - - const accessSql = ` - CREATE TABLE IF NOT EXISTS access ( - role_id INTEGER - NOT NULL, - secret_id INTEGER - NOT NULL, - FOREIGN KEY (role_id) - REFERENCES role(id) - ON DELETE CASCADE - ON UPDATE CASCADE, - FOREIGN KEY (secret_id) - REFERENCES secret(id) - ON DELETE CASCADE - ON UPDATE CASCADE - ) - ` - if _, err := db.Exec(accessSql); err != nil { - log.Fatal(err) - } - - txn, _ := db.Begin() - txn.Exec("INSERT INTO secret(raw_secret) VALUES (?)", []byte{0xff, 0xff}) - txn.Commit() -} - func Test_SQLiteEnv(t *testing.T) { db, err := sql.Open("sqlite3", "../../../build/env.db") if err != nil { t.Fatal(err) } - CreateTable(db) + + _, err = LoadSQLiteEnv(db) + if err != nil { + t.Fatal(err) + } } diff --git a/core/env/sql.go b/core/env/sql.go new file mode 100644 index 0000000..13a09ae --- /dev/null +++ b/core/env/sql.go @@ -0,0 +1 @@ +package env diff --git a/core/env/sqldb.go b/core/env/sqldb.go deleted file mode 100644 index 8d3d9fb..0000000 --- a/core/env/sqldb.go +++ /dev/null @@ -1,3 +0,0 @@ -package env - -type SQLDB interface{} From 3759bd483e6e4ab2a7f46fa29e1be0f73b0b0ae4 Mon Sep 17 00:00:00 2001 From: "Renol P. H." Date: Tue, 16 Apr 2024 20:21:57 +0700 Subject: [PATCH 3/7] fix: go mod tidy --- core/env/impl/sqliteenv.go | 2 ++ core/env/impl/sqliteenv_test.go | 2 -- go.mod | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/env/impl/sqliteenv.go b/core/env/impl/sqliteenv.go index b8f39a4..3ffb18c 100644 --- a/core/env/impl/sqliteenv.go +++ b/core/env/impl/sqliteenv.go @@ -2,6 +2,8 @@ package env_impl import ( "database/sql" + + _ "github.com/mattn/go-sqlite3" ) type SQLiteEnv struct { diff --git a/core/env/impl/sqliteenv_test.go b/core/env/impl/sqliteenv_test.go index 2ab30d3..2ce68bf 100644 --- a/core/env/impl/sqliteenv_test.go +++ b/core/env/impl/sqliteenv_test.go @@ -3,8 +3,6 @@ package env_impl import ( "database/sql" "testing" - - _ "github.com/mattn/go-sqlite3" ) func Test_SQLiteEnv(t *testing.T) { diff --git a/go.mod b/go.mod index c171e69..ce4d533 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,13 @@ go 1.22.0 require ( github.com/emirpasic/gods/v2 v2.0.0-alpha + github.com/mattn/go-sqlite3 v1.14.22 github.com/stretchr/testify v1.9.0 golang.org/x/crypto v0.22.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/objx v0.5.2 // indirect golang.org/x/sys v0.19.0 // indirect From fd45cf4ec724b1a25765e333de7678f36d4e8a93 Mon Sep 17 00:00:00 2001 From: "Renol P. H." Date: Tue, 16 Apr 2024 20:34:05 +0700 Subject: [PATCH 4/7] fix: sql syntax --- core/env/impl/sqliteenv.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/env/impl/sqliteenv.go b/core/env/impl/sqliteenv.go index 3ffb18c..31337c0 100644 --- a/core/env/impl/sqliteenv.go +++ b/core/env/impl/sqliteenv.go @@ -12,7 +12,7 @@ type SQLiteEnv struct { func LoadSQLiteEnv(db *sql.DB) (*SQLiteEnv, error) { const role = ` - CREATE TABLE IF NOT EXISTS role( + CREATE TABLE IF NOT EXISTS role ( id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(255) UNIQUE NOT NULL )` @@ -20,7 +20,7 @@ func LoadSQLiteEnv(db *sql.DB) (*SQLiteEnv, error) { return nil, err } const secret = ` - CREATE TABLE IF NOT EXISTS secret( + CREATE TABLE IF NOT EXISTS secret ( id INTEGER PRIMARY KEY NOT NULL, raw BLOB NOT NULL )` @@ -31,7 +31,7 @@ func LoadSQLiteEnv(db *sql.DB) (*SQLiteEnv, error) { CREATE TABLE IF NOT EXISTS access ( role_id INTEGER NOT NULL, secret_id INTEGER NOT NULL, - FOREIGN KEY(role_id) REFERENCES role(id) + FOREIGN KEY (role_id) REFERENCES role(id) ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (secret_id) REFERENCES secret(id) ON DELETE CASCADE ON UPDATE CASCADE From 86c5bcc2758605383469304378f26fb1103befcb Mon Sep 17 00:00:00 2001 From: "Renol P. H." Date: Tue, 16 Apr 2024 21:01:41 +0700 Subject: [PATCH 5/7] add: access index --- core/env/impl/sqliteenv.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/core/env/impl/sqliteenv.go b/core/env/impl/sqliteenv.go index 31337c0..ba0f4dd 100644 --- a/core/env/impl/sqliteenv.go +++ b/core/env/impl/sqliteenv.go @@ -11,23 +11,23 @@ type SQLiteEnv struct { } func LoadSQLiteEnv(db *sql.DB) (*SQLiteEnv, error) { - const role = ` + const roleTable = ` CREATE TABLE IF NOT EXISTS role ( id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(255) UNIQUE NOT NULL )` - if _, err := db.Exec(role); err != nil { + if _, err := db.Exec(roleTable); err != nil { return nil, err } - const secret = ` + const secretTable = ` CREATE TABLE IF NOT EXISTS secret ( id INTEGER PRIMARY KEY NOT NULL, raw BLOB NOT NULL )` - if _, err := db.Exec(secret); err != nil { + if _, err := db.Exec(secretTable); err != nil { return nil, err } - const access = ` + const accessTable = ` CREATE TABLE IF NOT EXISTS access ( role_id INTEGER NOT NULL, secret_id INTEGER NOT NULL, @@ -36,7 +36,13 @@ func LoadSQLiteEnv(db *sql.DB) (*SQLiteEnv, error) { FOREIGN KEY (secret_id) REFERENCES secret(id) ON DELETE CASCADE ON UPDATE CASCADE )` - if _, err := db.Exec(access); err != nil { + if _, err := db.Exec(accessTable); err != nil { + return nil, err + } + const accessIndex = ` + CREATE INDEX IF NOT EXISTS access_index + ON access (role_id, secret_id)` + if _, err := db.Exec(accessIndex); err != nil { return nil, err } env := &SQLiteEnv{db: db} From c9efdb1de9bd8a8f840ccd369db0149315effe23 Mon Sep 17 00:00:00 2001 From: "Renol P. H." Date: Wed, 17 Apr 2024 22:55:12 +0700 Subject: [PATCH 6/7] add: new table and index --- core/env/impl/sqliteenv.go | 79 +++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 14 deletions(-) diff --git a/core/env/impl/sqliteenv.go b/core/env/impl/sqliteenv.go index ba0f4dd..2a90bda 100644 --- a/core/env/impl/sqliteenv.go +++ b/core/env/impl/sqliteenv.go @@ -14,37 +14,88 @@ func LoadSQLiteEnv(db *sql.DB) (*SQLiteEnv, error) { const roleTable = ` CREATE TABLE IF NOT EXISTS role ( id INTEGER PRIMARY KEY NOT NULL, - name VARCHAR(255) UNIQUE NOT NULL + name VARCHAR(127) UNIQUE NOT NULL )` if _, err := db.Exec(roleTable); err != nil { return nil, err } - const secretTable = ` - CREATE TABLE IF NOT EXISTS secret ( + + const envTable = ` + CREATE TABLE IF NOT EXISTS env ( id INTEGER PRIMARY KEY NOT NULL, - raw BLOB NOT NULL + namespace VARCHAR(127) UNIQUE NOT NULL, + description TEXT )` - if _, err := db.Exec(secretTable); err != nil { + if _, err := db.Exec(envTable); err != nil { return nil, err } - const accessTable = ` - CREATE TABLE IF NOT EXISTS access ( + + const varTable = ` + CREATE TABLE IF NOT EXISTS var ( + env_id INTEGER NOT NULL, + name VARCHAR(127) NOT NULL, + value BLOB NOT NULL, + PRIMARY KEY (env_id, name), + FOREIGN KEY (env_id) REFERENCES env(id) + ON DELETE CASCADE ON UPDATE CASCADE + )` + if _, err := db.Exec(varTable); err != nil { + return nil, err + } + + const envAccessTable = ` + CREATE TABLE IF NOT EXISTS env_access ( role_id INTEGER NOT NULL, - secret_id INTEGER NOT NULL, + env_id INTEGER NOT NULL, + secret BLOB NOT NULL, FOREIGN KEY (role_id) REFERENCES role(id) ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY (secret_id) REFERENCES secret(id) + FOREIGN KEY (env_id) REFERENCES env(id) ON DELETE CASCADE ON UPDATE CASCADE )` - if _, err := db.Exec(accessTable); err != nil { + if _, err := db.Exec(envAccessTable); err != nil { return nil, err } - const accessIndex = ` - CREATE INDEX IF NOT EXISTS access_index - ON access (role_id, secret_id)` - if _, err := db.Exec(accessIndex); err != nil { + + const objectTable = ` + CREATE TABLE IF NOT EXISTS object ( + id INTEGER PRIMARY KEY NOT NULL, + name VARCHAR(127) UNIQUE NOT NULL, + description TEXT, + data BLOB NOT NULL + )` + if _, err := db.Exec(objectTable); err != nil { return nil, err } + + const objectAccessTable = ` + CREATE TABLE IF NOT EXISTS object_access ( + role_id INTEGER NOT NULL, + object_id INTEGER NOT NULL, + secret BLOB NOT NULL, + FOREIGN KEY (role_id) REFERENCES role(id) + ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (object_id) REFERENCES object(id) + ON DELETE CASCADE ON UPDATE CASCADE + )` + if _, err := db.Exec(objectAccessTable); err != nil { + return nil, err + } + + const envAccessIndex = ` + CREATE INDEX IF NOT EXISTS env_access_index + ON env_access (role_id, env_id)` + if _, err := db.Exec(envAccessIndex); err != nil { + return nil, err + } + + const objectAccessIndex = ` + CREATE INDEX IF NOT EXISTS object_access_index + ON object_access (role_id, object_id)` + if _, err := db.Exec(objectAccessIndex); err != nil { + return nil, err + } + env := &SQLiteEnv{db: db} return env, nil } From 49e873f7779a68c76d2bd676cd847ce9958dbdb3 Mon Sep 17 00:00:00 2001 From: "Renol P. H." Date: Sun, 21 Apr 2024 11:21:09 +0700 Subject: [PATCH 7/7] update: go.mod --- go.mod | 1 - go.sum | 2 -- 2 files changed, 3 deletions(-) diff --git a/go.mod b/go.mod index ce4d533..c57fa2c 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/reshifr/secure-env go 1.22.0 require ( - github.com/emirpasic/gods/v2 v2.0.0-alpha github.com/mattn/go-sqlite3 v1.14.22 github.com/stretchr/testify v1.9.0 golang.org/x/crypto v0.22.0 diff --git a/go.sum b/go.sum index c3a8829..9ad03a2 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emirpasic/gods/v2 v2.0.0-alpha h1:dwFlh8pBg1VMOXWGipNMRt8v96dKAIvBehtCt6OtunU= -github.com/emirpasic/gods/v2 v2.0.0-alpha/go.mod h1:W0y4M2dtBB9U5z3YlghmpuUhiaZT2h6yoeE+C1sCp6A= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=