Compare commits

...

4 Commits

59 changed files with 487 additions and 124 deletions

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
applicationservice_name:
type: String

View File

@ -5,8 +5,6 @@ description: |
pattern: rpc
public: true
parameters:
source_name:
type: String

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
applicationservice_name:
type: String

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
applicationservice_id:
type: Number

View File

@ -6,8 +6,6 @@ description: |
pattern: event
public: false
parameters:
server_id:
type: Number

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: false
parameters:
server_id:
type: Number

View File

@ -6,8 +6,6 @@ description: |
pattern: event
public: false
parameters:
server_id:
type: Number

View File

@ -5,8 +5,6 @@ description: Crée un serveur.
pattern: rpc
public: true
parameters:
server_name:
type: String

View File

@ -5,8 +5,6 @@ description: Un serveur a été créé.
pattern: event
public: false
parameters:
type: Server
description: Description du serveur.

View File

@ -6,8 +6,6 @@ description: |
pattern: event
public: false
parameters:
server_id:
type: Number

View File

@ -5,8 +5,6 @@ description: Retourne les attributs détaillés dun serveur.
pattern: rpc
public: true
parameters:
server_name:
type: String

View File

@ -4,8 +4,6 @@ uri: server.list
description: |
Liste les serveurs disponibles.
public: true
pattern: rpc
response:

View File

@ -5,8 +5,6 @@ description: Des modèles de serveur ont été créés.
pattern: event
public: false
parameters:
type: Servermodel
description: Informations sur les modèles de serveur créés.

View File

@ -5,8 +5,6 @@ description: |
pattern: rpc
public: true
parameters:
source_name:
type: String

View File

@ -5,8 +5,6 @@ description: Retourne les attributs détaillés d'un modèle de serveur suivant
pattern: rpc
public: false
parameters:
servermodel_id:
type: Number

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
source_id:
type: Number
@ -19,13 +17,3 @@ parameters:
response:
type: '[]Servermodel'
description: Liste des modèles de serveur disponibles.
errors:
- uri: servermodel.list.error.database_not_available
related:
- servermodel.describe
- servermodel.create
- servermodel.update
- servermodel.delete
- servermodel.event

View File

@ -5,8 +5,6 @@ description: Des modèles de serveur ont été modifiés.
pattern: event
public: false
parameters:
type: 'Servermodel'
description: Informations sur les modèles de serveur modifiés.

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
session_id:
type: String

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
session_id:
type: String

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
session_id:
type: String

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
response:
type: '[]Session'
description: |

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
id:
type: Number

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
session_id:
ref: Config.SessionId

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
session_id:
ref: Config.SessionId

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
session_id:
type: String

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
session_id:
type: String

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
session_id:
type: String

View File

@ -5,8 +5,6 @@ description: |
pattern: rpc
public: true
response:
type: '[]Session'
description: |

View File

@ -5,8 +5,6 @@ description: |
pattern: rpc
public: true
parameters:
id:
type: Number

View File

@ -5,8 +5,6 @@ description: |
pattern: rpc
public: true
parameters:
session_id:
ref: Config.SessionId

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
session_id:
ref: Config.SessionId

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
source_name:
type: String

View File

@ -5,8 +5,6 @@ description: |
pattern: rpc
public: true
parameters:
source_id:
type: Number

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
source_name:
type: String

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
response:
type: '[]Source'
description: Liste des sources disponibles.

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
source_id:
type: Number

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
source_id:
type: Number

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
release_id:
type: Number

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
response:
type: '[]Release'
description: Liste des versions disponibles.

View File

@ -6,8 +6,6 @@ description: |
pattern: rpc
public: true
parameters:
server_name:
type: String

View File

@ -0,0 +1,22 @@
---
uri: uri.role.join
description: Crée un rôle utilisateur.
pattern: rpc
parameters:
role_name:
type: String
shortarg: r
description: Nom du rôle.
ref: User.RoleName
uri_name:
type: String
shortarg: u
description: Nom du message.
response:
type: URIRole
description: Association de rôle créé.

View File

@ -0,0 +1,11 @@
---
uri: uri.role.list
description: Liste des associations d'URI et de rôle.
pattern: rpc
response:
type: '[]URIRole'
description: Liste des associations.

View File

@ -0,0 +1,25 @@
---
uri: user.create
description: Crée un utilisateur.
pattern: rpc
parameters:
user_login:
type: String
shortarg: l
description: Login de l'utilisateur.
ref: User.Login
user_name:
type: String
shortarg: n
description: Nom de l'utilisateur.
user_surname:
type: String
shortarg: s
description: Nom de famille de l'utilisateur.
response:
type: User
description: Description de l'utilisateur créé.

View File

@ -0,0 +1,18 @@
---
uri: user.delete
description: Supprimer un utilisateur.
pattern: rpc
parameters:
user_login:
type: String
shortarg: l
description: Login de l'utilisateur.
ref: User.Login
response:
type: User
description: Description de l'utilisateur supprimé.

View File

@ -0,0 +1,11 @@
---
uri: user.list
description: |
Liste les utilisateurs disponibles.
pattern: rpc
response:
type: '[]User'
description: Retourne la liste des utilisateurs.

View File

@ -0,0 +1,32 @@
---
uri: user.role.create
description: Crée un rôle utilisateur.
pattern: rpc
parameters:
user_login:
type: String
shortarg: l
description: Login de l'utilisateur.
ref: User.UserLogin
role_name:
type: String
shortarg: n
description: Nom du rôle.
ref: User.RoleName
role_attribute:
type: String
shortarg: a
description: Attribut contrôlé.
default: null
role_attribute_value:
type: String
shortarg: v
description: Valeur de l'attribut contrôlé.
default: null
response:
type: Role
description: Description du rôle créé.

View File

@ -0,0 +1,19 @@
---
uri: user.role.list
description: |
Liste les rôles disponibles.
pattern: rpc
parameters:
user_login:
type: String
shortarg: l
description: Login de l'utilisateur associé.
ref: User.RoleName
default: null
response:
type: '[]Role'
description: Retourne la liste des rôles.

View File

@ -0,0 +1,26 @@
---
title: Role
type: object
description: Description du rôle.
properties:
role_id:
type: number
description: Identifiant de l'utilisateur.
ref: User.RoleUserId
user_login:
type: string
description: Login du l'utilisateur.
ref: User.Login
role_name:
type: string
description: Nom du rôle.
role_attribute:
type: string
description: Nom de l'utilisateur.
role_attribute_value:
type: string
description: Valeur de l'attribut contrôlé.
required:
- role_id
- role_name

View File

@ -0,0 +1,15 @@
---
title: URIRole
type: object
description: Description de l'assication du message et du rôle.
properties:
role_name:
type: string
description: Nom du rôle
ref: User.RoleName
uri_name:
type: string
description: Nom du message
required:
- role_name
- uri_name

View File

@ -0,0 +1,24 @@
---
title: User
type: object
description: Description de l'utilisateur.
properties:
user_id:
type: number
description: Identifiant de l'utilisateur.
ref: User.UserId
user_login:
type: string
description: Login de l'utilisateur.
user_name:
type: string
description: Nom de l'utilisateur.
user_surname:
type: string
description: Nom de famille de l'utilisateur.
required:
- user_id
- user_login
- user_name
- user_surname

View File

@ -0,0 +1,20 @@
---
title: UserRole
type: object
description: Description de l'association du rôle et de l'utilisateur.
properties:
user_role_id:
type: number
description: Identifiant de l'association.
ref: User.UserRoleId
user_login:
type: string
description: Login de l'utilisateur.
role_name:
type: string
description: Nom du rôle.
required:
- user_role_id
- user_login
- role_name

View File

@ -92,9 +92,7 @@ class CallDispatcher:
if function_obj['database'] and hasattr(old_risotto_context, 'connection'):
risotto_context.connection = old_risotto_context.connection
if function_obj['database'] and not hasattr(risotto_context, 'connection'):
db_conf = get_config().get('database')
pool = await asyncpg.create_pool(database=db_conf.get('dbname'), user=db_conf.get('user'))
async with pool.acquire() as connection:
async with self.pool.acquire() as connection:
await connection.set_type_codec(
'json',
encoder=dumps,
@ -181,9 +179,7 @@ class PublishDispatcher:
if function_obj['database'] and hasattr(old_risotto_context, 'connection'):
risotto_context.connection = old_risotto_context.connection
if function_obj['database'] and not hasattr(risotto_context, 'connection'):
db_conf = get_config().get('database')
pool = await asyncpg.create_pool(database=db_conf.get('dbname'), user=db_conf.get('user'))
async with pool.acquire() as connection:
async with self.pool.acquire() as connection:
await connection.set_type_codec(
'json',
encoder=dumps,
@ -283,9 +279,7 @@ class Dispatcher(register.RegisterDispatcher, CallDispatcher, PublishDispatcher)
config: Config,
user_login: str,
uri: str) -> None:
db_conf = get_config().get('database')
pool = await asyncpg.create_pool(database=db_conf.get('dbname'), user=db_conf.get('user'))
async with pool.acquire() as connection:
async with self.pool.acquire() as connection:
async with connection.transaction():
# Verify if user exists and get ID
sql = '''

View File

@ -89,8 +89,7 @@ async def handle(request):
async def api(request, risotto_context):
global tiramisu
if not tiramisu:
config = await Config(get_messages(load_shortarg=True,
only_public=True)[1])
config = await Config(get_messages(load_shortarg=True)[1])
await config.property.read_write()
tiramisu = await config.option.dict(remotable='none')
return tiramisu
@ -113,11 +112,8 @@ async def get_app(loop):
print(_('======== Registered messages ========'))
for message in messages:
web_message = f'/api/{version}/{message}'
if dispatcher.messages[version][message]['public']:
print(f' - {web_message}')
else:
pattern = dispatcher.messages[version][message]['pattern']
print(f' - {web_message} (private {pattern})')
pattern = dispatcher.messages[version][message]['pattern']
print(f' - {web_message} ({pattern})')
routes.append(post(web_message, handle))
print()
print(_('======== Registered extra routes ========'))

View File

@ -44,7 +44,6 @@ class MessageDefinition:
'uri',
'description',
'parameters',
'public',
'errors',
'pattern',
'related',
@ -54,7 +53,6 @@ class MessageDefinition:
# default value for non mandatory key
self.version = u''
self.parameters = OrderedDict()
self.public = False
self.errors = []
self.related = []
self.response = None
@ -63,10 +61,7 @@ class MessageDefinition:
for key, value in raw_def.items():
if isinstance(value, str):
value = value.strip()
if key == 'public':
if not isinstance(value, bool):
raise ValueError(_("{} must be a boolean, not {}").format(key, value))
elif key == 'pattern':
if key == 'pattern':
if value not in ['rpc', 'event', 'error']:
raise Exception(_('unknown pattern {}').format(value))
elif key == 'parameters':
@ -86,9 +81,6 @@ class MessageDefinition:
getattr(self, key)
except AttributeError:
raise Exception(_('mandatory key not set {} message').format(key))
# message with pattern = error must be public
if self.public is False and self.pattern == 'error':
raise Exception(_('Error message must be public : {}').format(self.uri))
if self.uri != message:
raise Exception(_(f'yaml file name "{message}.yml" does not match uri "{self.uri}"'))
@ -581,7 +573,7 @@ def _get_root_option(select_option, optiondescriptions):
return OptionDescription('root', 'root', options_obj)
def get_messages(load_shortarg=False, only_public=False):
def get_messages(load_shortarg=False):
"""generate description from yml files
"""
optiondescriptions = OrderedDict()
@ -592,8 +584,7 @@ def get_messages(load_shortarg=False, only_public=False):
messages.sort()
for message_name in messages:
message_def = get_message(message_name)
if message_def.pattern not in ['rpc', 'event'] or \
(not message_def.public and only_public):
if message_def.pattern not in ['rpc', 'event']:
continue
optiondescriptions_name.append(message_def.uri)
optiondescriptions_name.sort()
@ -603,11 +594,9 @@ def get_messages(load_shortarg=False, only_public=False):
properties=frozenset(['mandatory', 'positional']))
for message_name in messages:
message_def = get_message(message_name)
if message_def.pattern not in ['rpc', 'event'] or \
(not message_def.public and only_public):
if message_def.pattern not in ['rpc', 'event']:
continue
optiondescriptions_info[message_def.uri] = {'pattern': message_def.pattern,
'public': message_def.public}
optiondescriptions_info[message_def.uri] = {'pattern': message_def.pattern}
if message_def.pattern == 'rpc':
optiondescriptions_info[message_def.uri]['response'] = _parse_responses(message_def,
message_name)

View File

@ -33,6 +33,8 @@ class RegisterDispatcher:
def __init__(self):
# reference to instanciate module (to inject self in method): {"module_name": instance_of_module}
self.injected_self = {}
# postgresql pool
self.pool = None
# list of uris with informations: {"v1": {"module_name.xxxxx": yyyyyy}}
self.messages = {}
# load tiramisu objects
@ -261,8 +263,8 @@ class RegisterDispatcher:
async def load(self):
# valid function's arguments
db_conf = get_config().get('database')
pool = await asyncpg.create_pool(database=db_conf.get('dbname'), user=db_conf.get('user'))
async with pool.acquire() as connection:
self.pool = await asyncpg.create_pool(database=db_conf.get('dbname'), user=db_conf.get('user'))
async with self.pool.acquire() as connection:
async with connection.transaction():
for version, messages in self.messages.items():
for message, message_infos in messages.items():

View File

@ -0,0 +1 @@
from .uri import Risotto

View File

@ -0,0 +1,100 @@
from typing import Dict, List
from ...controller import Controller
from ...register import register
from ...context import Context
from ...utils import _
class Risotto(Controller):
async def on_join(self,
risotto_context):
for uri in ['v1.applicationservice.create',
'v1.applicationservice.dataset.updated',
'v1.server.create',
'v1.servermodel.dataset.updated',
'v1.session.server.start',
'v1.source.create',
'v1.source.dataset.update',
'v1.source.release.create',
'v1.template.generate',
'v1.uri.role.join',
'v1.uri.role.list',
'v1.user.create',
'v1.user.delete',
'v1.user.list',
'v1.user.role.create',
'v1.user.role.list']:
try:
await self.call('v1.uri.role.join',
risotto_context,
role_name='administrator',
uri_name=uri)
except:
pass
for uri in ['v1.applicationservice.describe',
'v1.applicationservice.get_by_id',
'v1.server.describe',
'v1.server.list',
'v1.servermodel.list',
'v1.session.server.configure',
'v1.session.server.filter',
'v1.session.server.get',
'v1.session.server.list',
'v1.session.servermodel.configure',
'v1.session.servermodel.filter',
'v1.session.servermodel.get',
'v1.session.servermodel.list',
'v1.session.servermodel.start',
'v1.session.servermodel.stop',
'v1.session.servermodel.validate',
'v1.session.server.stop',
'v1.session.server.validate',
'v1.source.describe',
'v1.source.list',
'v1.source.release.get_by_distribution',
'v1.source.release.get_by_id',
'v1.source.release.list']:
try:
await self.call('v1.uri.role.join',
risotto_context,
role_name='all',
uri_name=uri)
except:
pass
@register('v1.uri.role.join', None, database=True)
async def uri_role_join(self,
risotto_context: Context,
role_name: str,
uri_name: str) -> Dict:
# Verify if user exists and get ID
sql = '''
SELECT URIId
FROM URI
WHERE URIName = $1
'''
uri_id = await risotto_context.connection.fetchval(sql,
uri_name)
if uri_id is None:
raise Exception(_(f'unable to find message {uri_name}'))
sql = '''
INSERT INTO RoleURI(RoleName, URIId)
VALUES ($1,$2)
ON CONFLICT DO NOTHING
'''
uri_id = await risotto_context.connection.fetchrow(sql,
role_name,
uri_id)
return {'role_name': role_name,
'uri_name': uri_name}
@register('v1.uri.role.list', None, database=True)
async def uri_role_list(self,
risotto_context: Context) -> List[Dict]:
sql = '''
SELECT RoleName as role_name, URI.URIName as uri
FROM RoleURI, URI
WHERE RoleURI.URIId = URI.URIId
'''
return [dict(r) for r in await risotto_context.connection.fetch(sql)]

View File

@ -0,0 +1 @@
from .user import Risotto

View File

@ -0,0 +1,147 @@
from typing import Dict, Optional
from ...controller import Controller
from ...register import register
from ...context import Context
from ...utils import _
class Risotto(Controller):
@register('v1.user.create', None, database=True)
async def user_create(self,
risotto_context: Context,
user_login: str,
user_name: str,
user_surname: str) -> Dict:
user_insert = """INSERT INTO RisottoUser(UserLogin, UserName, UserSurname)
VALUES ($1,$2,$3)
RETURNING UserId
"""
user_id = await risotto_context.connection.fetchval(user_insert,
user_login,
user_name,
user_surname)
await self.call('v1.user.role.create',
risotto_context,
user_login=user_login,
role_name='all')
return {'user_id': user_id,
'user_login': user_login,
'user_name': user_name,
'user_surname': user_surname}
@register('v1.user.list', None, database=True)
async def user_list(self,
risotto_context: Context) -> Dict:
sql = '''
SELECT UserId as user_id, UserLogin as user_login, UserName as user_name, UserSurname as user_surname
FROM RisottoUser
'''
users = await risotto_context.connection.fetch(sql)
return [dict(r) for r in users]
@register('v1.user.delete', None, database=True)
async def user_delete(self,
risotto_context: Context,
user_login: str) -> Dict:
sql = '''
DELETE FROM RisottoUser
WHERE UserLogin = $1
RETURNING UserId as user_id, UserLogin as user_login, UserName as user_name, UserSurname as user_surname
'''
user = await risotto_context.connection.fetchrow(sql,
user_login)
if user is None:
raise Exception(_(f'unable to find user {user_login}'))
return dict(user)
@register('v1.user.role.create', None, database=True)
async def user_role_create(self,
risotto_context: Context,
user_login: str,
role_name: str,
role_attribute: str,
role_attribute_value: str) -> Dict:
# Verify if user exists and get ID
sql = '''
SELECT UserId
FROM RisottoUser
WHERE UserLogin = $1
'''
user_id = await risotto_context.connection.fetchval(sql,
user_login)
if user_id is None:
raise Exception(_(f'unable to find user {user_login}'))
sql = '''INSERT INTO UserRole(RoleUserId, RoleName, RoleAttribute, RoleAttributeValue)
VALUES($1,$2,$3,$4)
RETURNING RoleId
'''
role_id = await risotto_context.connection.fetchval(sql,
user_id,
role_name,
role_attribute,
role_attribute_value)
return {'role_id': role_id,
'user_login': user_login,
'role_name': role_name,
'role_attribute': role_attribute,
'role_attribute_value': role_attribute_value}
@register('v1.user.role.list', None, database=True)
async def user_role_list(self,
risotto_context: Context,
user_login: Optional[str]) -> Dict:
if not user_login:
sql = '''
SELECT RoleId as role_id, RoleName as role_name, RoleAttribute as role_attribute, RoleAttributeValue as role_attribute_value, RisottoUser.UserLogin as user_login
FROM UserRole, RisottoUser
WHERE UserRole.RoleUserId = RisottoUser.UserId
'''
roles = await risotto_context.connection.fetch(sql)
else:
# Verify if user exists and get ID
sql = '''
SELECT UserId
FROM RisottoUser
WHERE UserLogin = $1
'''
user_id = await risotto_context.connection.fetchval(sql,
user_login)
if user_id is None:
raise Exception(_(f'unable to find user {user_login}'))
sql = '''
SELECT RoleId as role_id, RoleName as role_name, RoleAttribute as role_attribute, RoleAttributeValue as role_attribute_value, RisottoUser.UserLogin as user_login
FROM UserRole, RisottoUser
WHERE UserRole.RoleUserId = RisottoUser.UserId AND UserRole.RoleUserId = $1
'''
roles = await risotto_context.connection.fetch(sql,
user_id)
return [dict(r) for r in roles]
#
# FIXME comment savoir quel role il faut supprimer ? avec attribut ou juste l'ID ?
# @register('v1.user.role.delete', None, database=True)
# async def user_role_delete(self,
# risotto_context: Context,
# user_login: str,
# role_name: str) -> Dict:
# # Verify if user exists and get ID
# sql = '''
# SELECT UserId
# FROM RisottoUser
# WHERE UserLogin = $1
# '''
# user_id = await risotto_context.connection.fetchval(sql,
# user_login)
# if user_id is None:
# raise Exception(_(f'unable to find user {user_login}'))
# sql = '''
# DELETE FROM RisottoRole
# WHERE RoleName = $1 AND UserId = $2
# RETURNING RoleId as role_id, RoleName as role_name, RoleAttribute as role_attribute, RoleAttributeValue as role_attribute_value
# '''
# role = await risotto_context.connection.fetchrow(sql,
# role_name,
# user_id)
# if role is None:
# raise Exception(_(f'unable to find role {role_name}'))
# return dict(role)