mirror of
https://github.com/xcat2/confluent.git
synced 2026-01-11 18:42:29 +00:00
Add user functions to config db
This commit is contained in:
@@ -16,12 +16,13 @@
|
||||
# For multi-node operation, each instance opens and retains a TLS connection
|
||||
# to each other instance. 'set' operations push to queue for writeback and
|
||||
# returns. The writeback thread writes to local disk and to other instances.
|
||||
# A function is provided to wait for pending output to disk and peers to complete
|
||||
# to assure that aa neww requesst to peer does not beat configuration data to
|
||||
# the target
|
||||
# A function is provided to wait for pending output to disk and peers
|
||||
# to complete to assure that a new requesst to peer does not beat
|
||||
# configuration data to the target
|
||||
|
||||
# on disk format is cpickle. No data shall be in the configuration db required
|
||||
# to get started. For example, argv shall indicate ports rather than cfg store
|
||||
# TODO(jbjohnso): change to 'anydbm' scheme and actually tie things down
|
||||
|
||||
# Note on the cryptography. Default behavior is mostly just to pave the
|
||||
# way to meaningful security. Root all potentially sensitive data in
|
||||
@@ -31,11 +32,11 @@
|
||||
# - TPM sealing (which would forgo the interactive assuming risk of
|
||||
# physical attack on TPM is not a concern)
|
||||
|
||||
|
||||
import array
|
||||
import ast
|
||||
import collections
|
||||
import confluent.crypto
|
||||
import confluent.util
|
||||
import copy
|
||||
import cPickle
|
||||
import eventlet
|
||||
@@ -51,23 +52,50 @@ import threading
|
||||
_cfgstore = None
|
||||
|
||||
|
||||
def get_global(varname):
|
||||
if (_cfgstore is None or 'globals' not in _cfgstore or
|
||||
varname not in _cfgstore['globals']):
|
||||
return None
|
||||
return _cfgstore['globals'][varname]
|
||||
def get_global(globalname):
|
||||
"""Get a global variable
|
||||
|
||||
def set_global(varname, value):
|
||||
if _cfgstore is None:
|
||||
raise Exception("set_global called before config initted")
|
||||
|
||||
:param globalname: The global parameter name to read
|
||||
"""
|
||||
try:
|
||||
return _cfgstore['globals'][globalname]
|
||||
except:
|
||||
return None
|
||||
|
||||
def set_global(globalname, value):
|
||||
"""Set a global variable.
|
||||
|
||||
Globals should be rarely ever used. Almost everything should be under a
|
||||
tenant scope. Some things like master key and socket numbers/paths can be
|
||||
reasonably considered global in nature.
|
||||
|
||||
:param globalname: The global parameter name to store
|
||||
:param value: The value to set the global parameter to.
|
||||
"""
|
||||
if 'globals' not in _cfgstore:
|
||||
_cfgstore['globals'] = { varname: value }
|
||||
_cfgstore['globals'] = { globalname: value }
|
||||
else:
|
||||
_cfgstore['globals'][varname] = value
|
||||
_cfgstore['globals'][globalname] = value
|
||||
ConfigManager._bg_sync_to_file()
|
||||
|
||||
|
||||
def _generate_new_id():
|
||||
# generate a random id outside the usual ranges used for norml users in
|
||||
# /etc/passwd. Leave an equivalent amount of space near the end disused,
|
||||
# just in case
|
||||
id = confluent.util.securerandomnumber(65537, 4294901759)
|
||||
if 'idmap' not in _cfgstore:
|
||||
return id
|
||||
while id in _cfgstore['idmap']:
|
||||
id = confluent.util.securerandomnumber(65537, 4294901759)
|
||||
return id
|
||||
|
||||
class _ExpressionFormat(string.Formatter):
|
||||
# This class is used to extract the literal value from an expression
|
||||
# in the db
|
||||
# This is made easier by subclassing one of the 'fprintf' mechanisms
|
||||
# baked into python
|
||||
posmatch = re.compile('^n([0-9]*)$')
|
||||
nummatch = re.compile('[0-9]+')
|
||||
_supported_ops = {
|
||||
@@ -105,7 +133,7 @@ class _ExpressionFormat(string.Formatter):
|
||||
if '_expressionkeys' not in self._nodeobj:
|
||||
self._nodeobj['_expressionkeys'] = set([key])
|
||||
else:
|
||||
self._nodeobj['_expressionkeys'] .add([key])
|
||||
self._nodeobj['_expressionkeys'].add([key])
|
||||
val = _decode_attribute(key, self._nodeobj,
|
||||
formatter=self)
|
||||
return val['value'] if 'value' in val else ""
|
||||
@@ -156,27 +184,6 @@ def _decode_attribute(attribute, nodeobj, formatter=None, decrypt=False):
|
||||
return nodeobj[attribute]
|
||||
|
||||
|
||||
def get_tenant_id(tenantname):
|
||||
for tenant in _cfgstore['tenant'].iterkeys():
|
||||
if ('name' in _cfgstore['tenant'][tenant] and
|
||||
_cfgstore['tenant'][tenant]['name'] == tenantname):
|
||||
return tenant
|
||||
|
||||
|
||||
def get_tenant_names():
|
||||
global _cfgstore
|
||||
if _cfgstore is None or 'tenant' not in _cfgstore:
|
||||
return
|
||||
for tenant in _cfgstore['tenant'].iterkeys():
|
||||
if 'name' in _cfgstore['tenant'][tenant]:
|
||||
yield _cfgstore['tenant'][tenant]['name']
|
||||
|
||||
def get_user(name, tenant):
|
||||
global _cfgstore
|
||||
for user in _cfgstore['tenant'][tenant]['users'].iterkeys():
|
||||
if _cfgstore['tenant'][tenant]['users'][user]['name'] == user:
|
||||
return _cfgstore['tenant'][tenant]['users'][user]
|
||||
|
||||
# my thinking at this point is that noderange and configdata objects
|
||||
# will be constructed and passed as part of a context object to plugins
|
||||
# reasoning being that the main program will handle establishing the
|
||||
@@ -200,8 +207,57 @@ class ConfigManager(object):
|
||||
_cfgstore['tenant'] = {}
|
||||
if tenant not in _cfgstore['tenant']:
|
||||
_cfgstore['tenant'][tenant] = {'id': tenant}
|
||||
self.tenant = tenant
|
||||
self._cfgstore = _cfgstore['tenant'][tenant]
|
||||
|
||||
|
||||
def get_user(self, name):
|
||||
"""Get user information from DB
|
||||
|
||||
:param name: Name of the user
|
||||
|
||||
Returns a dictionary describing parameters of a user. These parameters
|
||||
may include numeric id (id), certificate thumbprint (certthumb),
|
||||
password hash (passhash, which currently is going to be PBKDF2 derived)
|
||||
full name (displayname), ...
|
||||
|
||||
"""
|
||||
try:
|
||||
return copy.deepcopy(self._cfgstore['users'][name])
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def create_user(self, name, role="Administrator", id=None, displayname=None):
|
||||
"""Create a new user
|
||||
|
||||
:param name: The login name of the user
|
||||
:param role: The role the user should be considered. Can be
|
||||
"Administrator" or "Technician", defaults to
|
||||
"Administrator"
|
||||
:param id: Custom identifier number if desired. Defaults to random.
|
||||
:param displayname: Optional long format name for UI consumption
|
||||
"""
|
||||
if id is None:
|
||||
id = _generate_new_id()
|
||||
else:
|
||||
if id in _cfgstore['idmap']:
|
||||
raise Exception("Duplicate id requested")
|
||||
if 'users' not in self._cfgstore:
|
||||
self._cfgstore['users'] = { }
|
||||
if name in self._cfgstore['users']:
|
||||
raise Exception("Duplicate username requested")
|
||||
self._cfgstore['users'][name] = {'id': id}
|
||||
if displayname is not None:
|
||||
self._cfgstore['users'][name]['displayname'] = displayname
|
||||
if 'idmap' not in _cfgstore:
|
||||
_cfgstore['idmap'] = {}
|
||||
_cfgstore['idmap'][id] = {
|
||||
'tenant': self.tenant,
|
||||
'username': name
|
||||
}
|
||||
self._bg_sync_to_file()
|
||||
|
||||
def get_node_attributes(self, nodelist, attributes=[]):
|
||||
if 'nodes' not in self._cfgstore:
|
||||
return None
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
# Various utility functions that do not neatly fit into one category or another
|
||||
import base64
|
||||
import os
|
||||
import struct
|
||||
|
||||
|
||||
def randomstring(length=20):
|
||||
"""Generate a random string of requested length
|
||||
@@ -14,3 +16,21 @@ def randomstring(length=20):
|
||||
chunksize += 1
|
||||
strval = base64.urlsafe_b64encode(os.urandom(chunksize * 3))
|
||||
return strval[0:length-1]
|
||||
|
||||
|
||||
def securerandomnumber(min=0, max=4294967295):
|
||||
"""Return a random number within requested range
|
||||
|
||||
Note that this function will not return smaller than 0 nor larger
|
||||
than 2^32-1 no matter what is requested.
|
||||
The python random number facility does not provide charateristics
|
||||
appropriate for secure rng, go to os.urandom
|
||||
|
||||
:param min: Smallest number to return (defaults to 0)
|
||||
:param max: largest number to return (defaults to 2^32-1)
|
||||
"""
|
||||
number = -1
|
||||
while number < min or number > max:
|
||||
number = struct.unpack("I",os.urandom(4))[0]
|
||||
return number
|
||||
|
||||
|
||||
Reference in New Issue
Block a user