netmaker/database/database.go
Vishal Dalwadi 614cf77b5a
NET-1991: Add IDP sync functionality. (#3428)
* feat: api access tokens

* revoke all user tokens

* redefine access token api routes, add auto egress option to enrollment keys

* add server settings apis, add db table for settigs

* handle server settings updates

* switch to using settings from DB

* fix sever settings migration

* revet force migration for settings

* fix server settings database write

* fix revoked tokens to be unauthorized

* remove unused functions

* convert access token to sql schema

* switch access token to sql schema

* fix merge conflicts

* fix server settings types

* bypass basic auth setting for super admin

* add TODO comment

* feat(go): add types for idp package;

* feat(go): import azure sdk;

* feat(go): add stub for google workspace client;

* feat(go): implement azure ad client;

* feat(go): sync users and groups using idp client;

* publish peer update on settings update

* feat(go): read creds from env vars;

* feat(go): add api endpoint to trigger idp sync;

* fix(go): sync member changes;

* fix(go): handle error;

* fix(go): set correct response type;

* feat(go): support disabling user accounts;

1. Add api endpoints to enable and disable user accounts.
2. Add checks in authenticators to prevent disabled users from logging in.
3. Add checks in middleware to prevent api usage by disabled users.

* feat(go): use string slice for group members;

* feat(go): sync user account status from idp;

* feat(go): import google admin sdk;

* feat(go): add support for google workspace idp;

* feat(go): initialize idp client on sync;

* feat(go): sync from idp periodically;

* feat(go): improvements for google idp;

1. Use the impersonate package to authenticate.
2. Use Pages method to get all data.

* chore(go): import style changes from migration branch;

1. Singular file names for table schema.
2. No table name method.
3. Use .Model instead of .Table.
4. No unnecessary tagging.

* remove nat check on egress gateway request

* Revert "remove nat check on egress gateway request"

This reverts commit 0aff12a189.

* feat(go): add db middleware;

* feat(go): restore method;

* feat(go): add user access token schema;

* fix user auth api:

* re initalise oauth and email config

* feat(go): fetch idp creds from server settings;

* feat(go): add filters for users and groups;

* feat(go): skip sync from idp if disabled;

* feat(go): add endpoint to remove idp integration;

* feat(go): import all users if no filters;

* feat(go): assign service-user role on sync;

* feat(go): remove microsoft-go-sdk;

* feat(go): add display name field for user;

* fix(go): set account disabled correctly;

* fix(go): update user if display name changes;

* fix(go): remove auth provider when removing idp integration;

* fix(go): ignore display name if empty;

* feat(go): add idp sync interval setting;

* fix(go): error on invalid auth provider;

* fix(go): no error if no user on group delete;

* fix(go): check superadmin using platform role id;

* feat(go): add display name and account disabled to return user as well;

* feat(go): tidy go mod after merge;

* feat(go): reinitialize auth provider and idp sync hook;

* fix(go): merge error;

* fix(go): merge error;

* feat(go): use id as the external provider id;

* fix(go): comments;

* feat(go): add function to return pending users;

* feat(go): prevent external id erasure;

* fix(go): user and group sync errors;

* chore(go): cleanup;

* fix(go): delete only oauth users;

* feat(go): use uuid group id;

* export ipd id to in rest api

* feat(go): don't use uuid for default groups;

* feat(go): migrate group only if id not uuid;

* chore(go): go mod tidy;

---------

Co-authored-by: abhishek9686 <abhi281342@gmail.com>
Co-authored-by: Abhishek K <abhishek@netmaker.io>
Co-authored-by: the_aceix <aceixsmartx@gmail.com>
2025-05-21 13:48:15 +05:30

233 lines
6.4 KiB
Go

package database
import (
"errors"
"sync"
"time"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/servercfg"
)
const (
// == Table Names ==
// NETWORKS_TABLE_NAME - networks table
NETWORKS_TABLE_NAME = "networks"
// NODES_TABLE_NAME - nodes table
NODES_TABLE_NAME = "nodes"
// DELETED_NODES_TABLE_NAME - deleted nodes table
DELETED_NODES_TABLE_NAME = "deletednodes"
// USERS_TABLE_NAME - users table
USERS_TABLE_NAME = "users"
// USER_PERMISSIONS_TABLE_NAME - user permissions table
USER_PERMISSIONS_TABLE_NAME = "user_permissions"
// CERTS_TABLE_NAME - certificates table
CERTS_TABLE_NAME = "certs"
// DNS_TABLE_NAME - dns table
DNS_TABLE_NAME = "dns"
// EXT_CLIENT_TABLE_NAME - ext client table
EXT_CLIENT_TABLE_NAME = "extclients"
// PEERS_TABLE_NAME - peers table
PEERS_TABLE_NAME = "peers"
// SERVERCONF_TABLE_NAME - stores server conf
SERVERCONF_TABLE_NAME = "serverconf"
// SERVER_UUID_TABLE_NAME - stores unique netmaker server data
SERVER_UUID_TABLE_NAME = "serveruuid"
// SERVER_UUID_RECORD_KEY - telemetry thing
SERVER_UUID_RECORD_KEY = "serveruuid"
// DATABASE_FILENAME - database file name
DATABASE_FILENAME = "netmaker.db"
// GENERATED_TABLE_NAME - stores server generated k/v
GENERATED_TABLE_NAME = "generated"
// NODE_ACLS_TABLE_NAME - stores the node ACL rules
NODE_ACLS_TABLE_NAME = "nodeacls"
// ACLS_TABLE_NAME - table for acls v2
ACLS_TABLE_NAME = "acls"
// SSO_STATE_CACHE - holds sso session information for OAuth2 sign-ins
SSO_STATE_CACHE = "ssostatecache"
// METRICS_TABLE_NAME - stores network metrics
METRICS_TABLE_NAME = "metrics"
// NETWORK_USER_TABLE_NAME - network user table tracks stats for a network user per network
NETWORK_USER_TABLE_NAME = "networkusers"
// USER_GROUPS_TABLE_NAME - table for storing usergroups
USER_GROUPS_TABLE_NAME = "usergroups"
// CACHE_TABLE_NAME - caching table
CACHE_TABLE_NAME = "cache"
// HOSTS_TABLE_NAME - the table name for hosts
HOSTS_TABLE_NAME = "hosts"
// ENROLLMENT_KEYS_TABLE_NAME - table name for enrollmentkeys
ENROLLMENT_KEYS_TABLE_NAME = "enrollmentkeys"
// HOST_ACTIONS_TABLE_NAME - table name for enrollmentkeys
HOST_ACTIONS_TABLE_NAME = "hostactions"
// PENDING_USERS_TABLE_NAME - table name for pending users
PENDING_USERS_TABLE_NAME = "pending_users"
// USER_INVITES - table for user invites
USER_INVITES_TABLE_NAME = "user_invites"
// TAG_TABLE_NAME - table for tags
TAG_TABLE_NAME = "tags"
// PEER_ACK_TABLE - table for failover peer ack
PEER_ACK_TABLE = "peer_ack"
// SERVER_SETTINGS - table for server settings
SERVER_SETTINGS = "server_settings"
// == ERROR CONSTS ==
// NO_RECORD - no singular result found
NO_RECORD = "no result found"
// NO_RECORDS - no results found
NO_RECORDS = "could not find any records"
// == DB Constants ==
// INIT_DB - initialize db
INIT_DB = "init"
// CREATE_TABLE - create table const
CREATE_TABLE = "createtable"
// INSERT - insert into db const
INSERT = "insert"
// INSERT_PEER - insert peer into db const
INSERT_PEER = "insertpeer"
// DELETE - delete db record const
DELETE = "delete"
// DELETE_ALL - delete a table const
DELETE_ALL = "deleteall"
// FETCH_ALL - fetch table contents const
FETCH_ALL = "fetchall"
// CLOSE_DB - graceful close of db const
CLOSE_DB = "closedb"
// isconnected
isConnected = "isconnected"
)
var dbMutex sync.RWMutex
var Tables = []string{
NETWORKS_TABLE_NAME,
NODES_TABLE_NAME,
CERTS_TABLE_NAME,
DELETED_NODES_TABLE_NAME,
USERS_TABLE_NAME,
DNS_TABLE_NAME,
EXT_CLIENT_TABLE_NAME,
PEERS_TABLE_NAME,
SERVERCONF_TABLE_NAME,
SERVER_UUID_TABLE_NAME,
GENERATED_TABLE_NAME,
NODE_ACLS_TABLE_NAME,
SSO_STATE_CACHE,
METRICS_TABLE_NAME,
NETWORK_USER_TABLE_NAME,
USER_GROUPS_TABLE_NAME,
CACHE_TABLE_NAME,
HOSTS_TABLE_NAME,
ENROLLMENT_KEYS_TABLE_NAME,
HOST_ACTIONS_TABLE_NAME,
PENDING_USERS_TABLE_NAME,
USER_PERMISSIONS_TABLE_NAME,
USER_INVITES_TABLE_NAME,
TAG_TABLE_NAME,
ACLS_TABLE_NAME,
PEER_ACK_TABLE,
SERVER_SETTINGS,
}
func getCurrentDB() map[string]interface{} {
switch servercfg.GetDB() {
case "rqlite":
return RQLITE_FUNCTIONS
case "sqlite":
return SQLITE_FUNCTIONS
case "postgres":
return PG_FUNCTIONS
default:
return SQLITE_FUNCTIONS
}
}
// InitializeDatabase - initializes database
func InitializeDatabase() error {
logger.Log(0, "connecting to", servercfg.GetDB())
tperiod := time.Now().Add(10 * time.Second)
for {
if err := getCurrentDB()[INIT_DB].(func() error)(); err != nil {
logger.Log(0, "unable to connect to db, retrying . . .")
if time.Now().After(tperiod) {
return err
}
} else {
break
}
time.Sleep(2 * time.Second)
}
createTables()
return nil
}
func createTables() {
for _, table := range Tables {
_ = CreateTable(table)
}
}
func CreateTable(tableName string) error {
return getCurrentDB()[CREATE_TABLE].(func(string) error)(tableName)
}
// Insert - inserts object into db
func Insert(key string, value string, tableName string) error {
dbMutex.Lock()
defer dbMutex.Unlock()
if key != "" && value != "" {
return getCurrentDB()[INSERT].(func(string, string, string) error)(key, value, tableName)
} else {
return errors.New("invalid insert " + key + " : " + value)
}
}
// DeleteRecord - deletes a record from db
func DeleteRecord(tableName string, key string) error {
dbMutex.Lock()
defer dbMutex.Unlock()
return getCurrentDB()[DELETE].(func(string, string) error)(tableName, key)
}
// DeleteAllRecords - removes a table and remakes
func DeleteAllRecords(tableName string) error {
dbMutex.Lock()
defer dbMutex.Unlock()
err := getCurrentDB()[DELETE_ALL].(func(string) error)(tableName)
if err != nil {
return err
}
err = CreateTable(tableName)
if err != nil {
return err
}
return nil
}
// FetchRecord - fetches a record
func FetchRecord(tableName string, key string) (string, error) {
results, err := FetchRecords(tableName)
if err != nil {
return "", err
}
if results[key] == "" {
return "", errors.New(NO_RECORD)
}
return results[key], nil
}
// FetchRecords - fetches all records in given table
func FetchRecords(tableName string) (map[string]string, error) {
dbMutex.RLock()
defer dbMutex.RUnlock()
return getCurrentDB()[FETCH_ALL].(func(string) (map[string]string, error))(tableName)
}
// CloseDB - closes a database gracefully
func CloseDB() {
getCurrentDB()[CLOSE_DB].(func())()
}
// IsConnected - tell if the database is connected or not
func IsConnected() bool {
return getCurrentDB()[isConnected].(func() bool)()
}