Fix race in Postgres user-list
Retrieve the user names and access rights in a single query hence closing the race window. Change-Id: I66847827fa8cb0615c1662670017d2cc55466ff3 Closes-Bug: 1617464
This commit is contained in:
parent
4c1c191def
commit
5c4b710000
@ -0,0 +1,4 @@
|
||||
---
|
||||
fixes:
|
||||
- Close the race condition window in user-list
|
||||
call. Closes-Bug 1617464
|
@ -129,13 +129,15 @@ class UserQuery(object):
|
||||
def list(cls, ignore=()):
|
||||
"""Query to list all users."""
|
||||
|
||||
statement = "SELECT usename FROM pg_catalog.pg_user"
|
||||
statement = (
|
||||
"SELECT usename, datname, pg_encoding_to_char(encoding), "
|
||||
"datcollate FROM pg_catalog.pg_user "
|
||||
"LEFT JOIN pg_catalog.pg_database "
|
||||
"ON CONCAT(usename, '=CTc/os_admin') = ANY(datacl::text[]) "
|
||||
"WHERE (datistemplate ISNULL OR datistemplate = false)")
|
||||
if ignore:
|
||||
# User a simple tautology so all clauses can be AND'ed without
|
||||
# crazy special logic.
|
||||
statement += " WHERE 1=1"
|
||||
for name in ignore:
|
||||
statement += " AND usename != '{name}'".format(name=name)
|
||||
for name in ignore:
|
||||
statement += " AND usename != '{name}'".format(name=name)
|
||||
|
||||
return statement
|
||||
|
||||
@ -156,10 +158,7 @@ class UserQuery(object):
|
||||
def get(cls, name):
|
||||
"""Query to get a single user."""
|
||||
|
||||
return (
|
||||
"SELECT usename FROM pg_catalog.pg_user "
|
||||
"WHERE usename = '{name}'".format(name=name)
|
||||
)
|
||||
return cls.list() + " AND usename = '{name}'".format(name=name)
|
||||
|
||||
@classmethod
|
||||
def create(cls, name, password, encrypt_password=None, *options):
|
||||
@ -226,18 +225,6 @@ class UserQuery(object):
|
||||
|
||||
class AccessQuery(object):
|
||||
|
||||
@classmethod
|
||||
def list(cls, user):
|
||||
"""Query to list grants for a user."""
|
||||
|
||||
return (
|
||||
"SELECT datname, pg_encoding_to_char(encoding), datcollate "
|
||||
"FROM pg_database "
|
||||
"WHERE datistemplate = false "
|
||||
"AND 'user \"{user}\"=CTc/{admin}' = ANY (datacl)".format(
|
||||
user=user, admin=PG_ADMIN)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def grant(cls, user, database):
|
||||
"""Query to grant user access to a database."""
|
||||
|
@ -19,7 +19,6 @@ from trove.common import cfg
|
||||
from trove.common import exception
|
||||
from trove.common.i18n import _
|
||||
from trove.guestagent.datastore.experimental.postgresql import pgutil
|
||||
from trove.guestagent.db import models
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
@ -78,17 +77,8 @@ class PgSqlAccess(object):
|
||||
Return a list of serialized Postgres databases.
|
||||
"""
|
||||
|
||||
if self.user_exists(username):
|
||||
return [db.serialize() for db in self._get_databases_for(username)]
|
||||
user = self._find_user(context, username)
|
||||
if user is not None:
|
||||
return user.databases
|
||||
|
||||
raise exception.UserNotFound(username)
|
||||
|
||||
def _get_databases_for(self, username):
|
||||
"""Return all Postgres databases accessible by a given user."""
|
||||
results = pgutil.query(
|
||||
pgutil.AccessQuery.list(user=username),
|
||||
timeout=30,
|
||||
)
|
||||
return [models.PostgreSQLSchema(
|
||||
row[0].strip(), character_set=row[1], collate=row[2])
|
||||
for row in results]
|
||||
|
@ -141,16 +141,23 @@ class PgSqlUsers(PgSqlAccess):
|
||||
pgutil.UserQuery.list(ignore=cfg.get_ignored_users()),
|
||||
timeout=30,
|
||||
)
|
||||
return [self._build_user(context, row[0].strip()) for row in results]
|
||||
|
||||
def _build_user(self, context, username):
|
||||
names = set([row[0].strip() for row in results])
|
||||
return [self._build_user(context, name, results) for name in names]
|
||||
|
||||
def _build_user(self, context, username, acl=None):
|
||||
"""Build a model representation of a Postgres user.
|
||||
Include all databases it has access to.
|
||||
"""
|
||||
user = models.PostgreSQLUser(username)
|
||||
dbs = self.list_access(context, username, None)
|
||||
for d in dbs:
|
||||
user.databases.append(d)
|
||||
if acl:
|
||||
dbs = [models.PostgreSQLSchema(row[1].strip(),
|
||||
character_set=row[2],
|
||||
collate=row[3])
|
||||
for row in acl if row[0] == username and row[1] is not None]
|
||||
for d in dbs:
|
||||
user.databases.append(d.serialize())
|
||||
|
||||
return user
|
||||
|
||||
def delete_user(self, context, user):
|
||||
@ -199,7 +206,7 @@ class PgSqlUsers(PgSqlAccess):
|
||||
)
|
||||
|
||||
if results:
|
||||
return self._build_user(context, username)
|
||||
return self._build_user(context, username, results)
|
||||
|
||||
return None
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user