146 lines
3.0 KiB
Go
146 lines
3.0 KiB
Go
|
package abac
|
||
|
|
||
|
import (
|
||
|
"database/sql"
|
||
|
|
||
|
"github.com/google/uuid"
|
||
|
_ "github.com/mattn/go-sqlite3"
|
||
|
)
|
||
|
|
||
|
type SQLiteStore struct {
|
||
|
db *sql.DB
|
||
|
}
|
||
|
|
||
|
func NewSQLiteStore(dbPath string) (*SQLiteStore, error) {
|
||
|
db, err := sql.Open("sqlite3", dbPath)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
store := &SQLiteStore{db: db}
|
||
|
err = store.createTables(db)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return store, nil
|
||
|
}
|
||
|
|
||
|
func (s *SQLiteStore) Close() error {
|
||
|
return s.db.Close()
|
||
|
}
|
||
|
|
||
|
func (s *SQLiteStore) GetUserAttributes(userID string) ([]Attribute, error) {
|
||
|
rows, err := s.db.Query(`
|
||
|
SELECT a.key, a.value
|
||
|
FROM attributes a
|
||
|
JOIN user_attributes ua ON a.id = ua.attribute_id
|
||
|
WHERE ua.user_id = ?;
|
||
|
`, userID)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defer rows.Close()
|
||
|
|
||
|
var attributes []Attribute
|
||
|
|
||
|
for rows.Next() {
|
||
|
var attribute Attribute
|
||
|
err := rows.Scan(&attribute.Key, &attribute.Value)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
attributes = append(attributes, attribute)
|
||
|
}
|
||
|
|
||
|
return attributes, nil
|
||
|
}
|
||
|
|
||
|
func (s *SQLiteStore) GetResourceAttributes(resourceID string) ([]Attribute, error) {
|
||
|
rows, err := s.db.Query(`
|
||
|
SELECT a.key, a.value
|
||
|
FROM attributes a
|
||
|
JOIN resource_attributes ra ON a.id = ra.attribute_id
|
||
|
WHERE ra.resource_id = ?;
|
||
|
`, resourceID)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defer rows.Close()
|
||
|
|
||
|
var attributes []Attribute
|
||
|
|
||
|
for rows.Next() {
|
||
|
var attribute Attribute
|
||
|
err := rows.Scan(&attribute.Key, &attribute.Value)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
attributes = append(attributes, attribute)
|
||
|
}
|
||
|
|
||
|
return attributes, nil
|
||
|
}
|
||
|
|
||
|
func newID() string {
|
||
|
uuid, err := uuid.NewV7()
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
return uuid.String()
|
||
|
}
|
||
|
|
||
|
func (s *SQLiteStore) CreatePolicy(effect, action, conditionAttributeKey, conditionAttributeValue string) error {
|
||
|
_, err := s.db.Exec(`
|
||
|
INSERT INTO policies (id, effect, action, condition_attribute_key, condition_attribute_value)
|
||
|
VALUES (?, ?, ?, ?, ?);
|
||
|
`, newID(), effect, action, conditionAttributeKey, conditionAttributeValue)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
func (s *SQLiteStore) createTables(db *sql.DB) error {
|
||
|
_, err := db.Exec(`
|
||
|
CREATE TABLE IF NOT EXISTS users (
|
||
|
id TEXT PRIMARY KEY,
|
||
|
username TEXT NOT NULL
|
||
|
);
|
||
|
|
||
|
CREATE TABLE IF NOT EXISTS resources (
|
||
|
id TEXT PRIMARY KEY,
|
||
|
name TEXT NOT NULL
|
||
|
);
|
||
|
|
||
|
CREATE TABLE IF NOT EXISTS attributes (
|
||
|
id TEXT PRIMARY KEY,
|
||
|
key TEXT NOT NULL,
|
||
|
value TEXT NOT NULL
|
||
|
);
|
||
|
|
||
|
CREATE TABLE IF NOT EXISTS user_attributes (
|
||
|
user_id TEXT NOT NULL,
|
||
|
attribute_id INTEGER NOT NULL,
|
||
|
FOREIGN KEY (user_id) REFERENCES users(id),
|
||
|
FOREIGN KEY (attribute_id) REFERENCES attributes(id)
|
||
|
);
|
||
|
|
||
|
CREATE TABLE IF NOT EXISTS resource_attributes (
|
||
|
resource_id TEXT NOT NULL,
|
||
|
attribute_id TEXT NOT NULL,
|
||
|
FOREIGN KEY (resource_id) REFERENCES resources(id),
|
||
|
FOREIGN KEY (attribute_id) REFERENCES attributes(id)
|
||
|
);
|
||
|
|
||
|
CREATE TABLE IF NOT EXISTS policies (
|
||
|
id TEXT PRIMARY KEY,
|
||
|
effect TEXT NOT NULL CHECK(effect IN ('Allow', 'Deny')),
|
||
|
action TEXT NOT NULL,
|
||
|
condition_attribute_key TEXT,
|
||
|
condition_attribute_value TEXT
|
||
|
);
|
||
|
`)
|
||
|
return err
|
||
|
}
|