Skip to content
This repository was archived by the owner on Jan 15, 2019. It is now read-only.

Commit a03b78b

Browse files
committed
Serverside fixes for endpoint arguments
Passing a datetime object would crash the serializer. This is the first half of an implementation to solve that problem by providing task arguments to the client.
1 parent 473693c commit a03b78b

File tree

14 files changed

+549
-232
lines changed

14 files changed

+549
-232
lines changed

CssefClient/cssef-cli

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@ class Menu(object):
1616
# Start by looping over the endpoint sources
1717
for source in self.source_list:
1818
for endpoint in source['endpoints']:
19-
self.menu_dict[endpoint['menu_path']] = \
20-
RPCEndpoint(self.config, endpoint['rpc_name'])
19+
command_inst = RPCEndpoint(
20+
self.config,
21+
endpoint['rpc_name'],
22+
endpoint['args'])
23+
self.menu_dict[endpoint['menu_path']] = command_inst
24+
2125

2226
def get_command(self, command_list):
2327
"""Gets the selected command from the menu dictionary
@@ -174,11 +178,16 @@ if __name__ == "__main__":
174178
client.load_endpoints()
175179
client.load_token(auth=user_input.command_args['auth'])
176180

177-
# Call command and handle return
181+
# Build the command menu and instantiate the RCPEndpoint we'll be calling
178182
commandMenu = Menu(client.config, client.endpoints)
179183
commandMenu.construct()
180-
181184
command = commandMenu.get_command(user_input.command)
185+
186+
# Make sure our provided input matches what we were told to send to the client
187+
if not command.validate_input(user_input.command_args):
188+
print "Invalid arguments"
189+
sys.exit()
190+
182191
output_obj = client.call_endpoint(command, user_input.command_args)
183192
print_output(output_obj)
184193
sys.exit(output_obj.value)

CssefClient/cssefclient/utils.py

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import re
33
import sys
44
import stat
5+
import time
6+
from datetime import datetime
57

68
class CommandOutput(object):
79
def __init__(self, value, message, content):
@@ -36,7 +38,7 @@ class RPCEndpoint(object):
3638
This class gets subclassed by other classes to define a specific
3739
task on the cssef server.
3840
"""
39-
def __init__(self, config, rpc_name):
41+
def __init__(self, config, rpc_name, args = []):
4042
"""
4143
Args:
4244
config (Configuration): The current configuration to use
@@ -50,6 +52,14 @@ def __init__(self, config, rpc_name):
5052
"""
5153
self.config = config
5254
self.rpc_name = rpc_name
55+
self.args = args
56+
57+
def validate_input(self, user_input):
58+
for i in self.args:
59+
if i['required'] and i['name'] not in user_input:
60+
# Argument missing from the input
61+
return False
62+
return True
5363

5464
def execute(self, **kwargs):
5565
"""Calls the rpc endpoint on the remote server
@@ -66,24 +76,22 @@ def execute(self, **kwargs):
6676
"""
6777
if self.config.verbose:
6878
print "[LOGGING] Calling rpc with name '%s'." % self.rpc_name
79+
# "Serialize" datetime objects
80+
for i in kwargs:
81+
if isinstance(kwargs[i], datetime):
82+
print "Found datetime object"
83+
# Overwrite the datetime object to a unix timestamp representing the same time
84+
kwargs[i] = time.mktime(kwargs[i].timetuple())
85+
# Try the api call
6986
try:
70-
# This is a hint at a larger issue- If I don't cast this to an
71-
# integer, it is passed to send_task() and get() as a string
72-
# rather than an expected integer. This means all values read
73-
# from the configuration object are strings, which may be
74-
# problematic if a value MUST be an integer.
75-
#task_timeout = int(self.config.task_timeout)
76-
#self.task = self.config.apiConn.send_task(self.endpointName,
77-
# args = args, kwargs = kwargs, expires = task_timeout)
78-
#result = self.task.get(timeout = task_timeout)
7987
output_dict = self.config.server_connection.request(self.rpc_name, **kwargs)
80-
if output_dict:
81-
return CommandOutput(**output_dict)
82-
else:
83-
return CommandOutput(value=-1, content=[], \
84-
message=['Call to endpoint returned "None".'])
8588
except Exception as err:
8689
return CommandOutput(value=-1, content=[], message=[str(err)])
90+
# Handle the returned api data
91+
if output_dict:
92+
return CommandOutput(**output_dict)
93+
return CommandOutput(value=-1, content=[], \
94+
message=["Call to endpoint '%s' returned 'None'." % self.rpc_name])
8795

8896
def load_token_file(token_file):
8997
"""Load token from a file

CssefServer/cssefserver/__init__.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,18 +121,18 @@ def __init__(self, config):
121121

122122
@classmethod
123123
def endpoint_info(cls):
124-
tmp_dict = {}
125-
tmp_dict['name'] = cls.__name__
124+
tmp_dict = cls.as_dict()
126125
tmp_dict['endpoints'] = []
127126
for endpoint in cls.endpoints:
128127
tmp_dict['endpoints'].append(endpoint.info_dict())
129128
return tmp_dict
130129

131-
def as_dict(self):
130+
@classmethod
131+
def as_dict(cls):
132132
tmp_dict = {}
133-
tmp_dict['name'] = self.name
134-
tmp_dict['short_name'] = self.short_name
135-
tmp_dict['version'] = self.__version__
133+
tmp_dict['name'] = cls.name
134+
tmp_dict['short_name'] = cls.short_name
135+
tmp_dict['version'] = cls.__version__
136136
return tmp_dict
137137

138138
# Dumpy old logging methods

CssefServer/cssefserver/account/tasks.py

Lines changed: 61 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import logging
12
from cssefserver.utils import get_empty_return_dict
23
from cssefserver.utils import CssefRPCEndpoint
34
from cssefserver.taskutils import model_del
@@ -12,8 +13,13 @@ class OrganizationAdd(CssefRPCEndpoint):
1213
name = "Organization Add"
1314
rpc_name = "organizationadd"
1415
menu_path = "organization.add"
15-
onRequestArgs = ['auth']
16-
def on_request(self, auth, **kwargs):
16+
args = [
17+
{'name': 'name', 'type': 'str', 'required': True},
18+
{'name': 'description', 'type': 'str', 'required': True},
19+
{'name': 'can_add_users', 'type': 'bool', 'required': False},
20+
{'name': 'can_delete_users', 'type': 'bool', 'required': False}]
21+
22+
def on_request(self, **kwargs):
1723
"""Celery task to create a new organization.
1824
1925
Args:
@@ -23,10 +29,7 @@ def on_request(self, auth, **kwargs):
2329
A return_dict dictionary containing the results of the API call. See
2430
get_empty_return_dict for more information.
2531
"""
26-
try:
27-
authorize_access(self.database_connection, auth, self.config)
28-
except CssefException as err:
29-
return err.as_return_dict()
32+
authorize_access(self.database_connection, self.auth, self.config)
3033
organization = Organization.from_dict(self.database_connection, kwargs)
3134
return_dict = get_empty_return_dict()
3235
return_dict['content'].append(organization.as_dict())
@@ -36,9 +39,10 @@ class OrganizationDel(CssefRPCEndpoint):
3639
name = "Organization Delete"
3740
rpc_name = "organizationdel"
3841
menu_path = "organization.del"
39-
takesKwargs = False
40-
onRequestArgs = ['auth', 'pkid']
41-
def on_request(self, auth, pkid):
42+
args = [
43+
{'name': 'pkid', 'type': 'int', 'required': True}]
44+
45+
def on_request(self, pkid):
4246
"""Celery task to delete an existing organization.
4347
4448
Args:
@@ -48,15 +52,21 @@ def on_request(self, auth, pkid):
4852
A return_dict dictionary containing the results of the API call. See
4953
get_empty_return_dict for more information.
5054
"""
51-
authorize_access(self.database_connection, auth, self.config)
55+
authorize_access(self.database_connection, self.auth, self.config)
5256
return model_del(Organization, self.database_connection, pkid)
5357

5458
class OrganizationSet(CssefRPCEndpoint):
5559
name = "Organization Set"
5660
rpc_name = "organizationset"
5761
menu_path = "organization.set"
58-
onRequestArgs = ['auth', 'pkid']
59-
def on_request(self, auth, pkid, **kwargs):
62+
args = [
63+
{'name': 'pkid', 'type': 'int', 'required': True},
64+
{'name': 'name', 'type': 'str', 'required': False},
65+
{'name': 'description', 'type': 'str', 'required': False},
66+
{'name': 'can_add_users', 'type': 'bool', 'required': False},
67+
{'name': 'can_delete_users', 'type': 'bool', 'required': False}]
68+
69+
def on_request(self, pkid, **kwargs):
6070
"""Celery task to edit an existing organization.
6171
6272
Args:
@@ -67,15 +77,21 @@ def on_request(self, auth, pkid, **kwargs):
6777
A return_dict dictionary containing the results of the API call. See
6878
get_empty_return_dict for more information.
6979
"""
70-
authorize_access(self.database_connection, auth, self.config)
80+
authorize_access(self.database_connection, self.auth, self.config)
7181
return model_set(Organization, self.database_connection, pkid, **kwargs)
7282

7383
class OrganizationGet(CssefRPCEndpoint):
7484
name = "Organization Get"
7585
rpc_name = "organizationget"
7686
menu_path = "organization.get"
77-
onRequestArgs = ['auth']
78-
def on_request(self, auth, **kwargs):
87+
args = [
88+
{'name': 'pkid', 'type': 'str', 'required': False},
89+
{'name': 'name', 'type': 'str', 'required': False},
90+
{'name': 'description', 'type': 'str', 'required': False},
91+
{'name': 'can_add_users', 'type': 'bool', 'required': False},
92+
{'name': 'can_delete_users', 'type': 'bool', 'required': False}]
93+
94+
def on_request(self, **kwargs):
7995
"""Celery task to get one or more existing organization.
8096
8197
Args:
@@ -85,15 +101,20 @@ def on_request(self, auth, **kwargs):
85101
A return_dict dictionary containing the results of the API call. See
86102
get_empty_return_dict for more information.
87103
"""
88-
authorize_access(self.database_connection, auth, self.config)
104+
authorize_access(self.database_connection, self.auth, self.config)
89105
return model_get(Organization, self.database_connection, **kwargs)
90106

91107
class UserAdd(CssefRPCEndpoint):
92108
name = "User Add"
93109
rpc_name = "useradd"
94110
menu_path = "user.add"
95-
onRequestArgs = ['auth']
96-
def on_request(self, auth, **kwargs):
111+
args = [
112+
{'name': 'name', 'type': 'str', 'required': True},
113+
{'name': 'username', 'type': 'str', 'required': True},
114+
{'name': 'password', 'type': 'str', 'required': True},
115+
{'name': 'organization', 'type': 'int', 'required': True}]
116+
117+
def on_request(self, **kwargs):
97118
"""Celery task to create a new user.
98119
99120
Args:
@@ -105,7 +126,7 @@ def on_request(self, auth, **kwargs):
105126
get_empty_return_dict for more information.
106127
"""
107128
try:
108-
authorize_access(self.database_connection, auth, self.config)
129+
authorize_access(self.database_connection, self.auth, self.config)
109130
except CssefException as err:
110131
return err.as_return_dict()
111132
#kwargs['organization'] = organization
@@ -118,9 +139,10 @@ class UserDel(CssefRPCEndpoint):
118139
name = "User Delete"
119140
rpc_name = "userdel"
120141
menu_path = "user.del"
121-
takesKwargs = False
122-
onRequestArgs = ['auth', 'pkid']
123-
def on_request(self, auth, pkid):
142+
args = [
143+
{'name': 'pkid', 'type': 'int', 'required': True}]
144+
145+
def on_request(self, pkid):
124146
"""Celery task to delete an existing user.
125147
126148
Args:
@@ -130,15 +152,20 @@ def on_request(self, auth, pkid):
130152
A return_dict dictionary containing the results of the API call. See
131153
get_empty_return_dict for more information.
132154
"""
133-
authorize_access(self.database_connection, auth, self.config)
155+
authorize_access(self.database_connection, self.auth, self.config)
134156
return model_del(User, self.database_connection, pkid)
135157

136158
class UserSet(CssefRPCEndpoint):
137159
name = "User Set"
138160
rpc_name = "userset"
139161
menu_path = "user.set"
140-
onRequestArgs = ['auth', 'pkid']
141-
def on_request(self, auth, pkid, **kwargs):
162+
args = [
163+
{'name': 'pkid', 'type': 'int', 'required': True},
164+
{'name': 'name', 'type': 'str', 'required': False},
165+
{'name': 'username', 'type': 'str', 'required': False},
166+
{'name': 'password', 'type': 'str', 'required': False}]
167+
168+
def on_request(self, pkid, **kwargs):
142169
"""Celery task to edit an existing user.
143170
144171
Args:
@@ -149,15 +176,20 @@ def on_request(self, auth, pkid, **kwargs):
149176
A return_dict dictionary containing the results of the API call. See
150177
get_empty_return_dict for more information.
151178
"""
152-
authorize_access(self.database_connection, auth, self.config)
179+
authorize_access(self.database_connection, self.auth, self.config)
153180
return model_set(User, self.database_connection, pkid, **kwargs)
154181

155182
class UserGet(CssefRPCEndpoint):
156183
name = "User Get"
157184
rpc_name = "userget"
158185
menu_path = "user.get"
159-
onRequestArgs = ['auth']
160-
def on_request(self, auth, **kwargs):
186+
args = [
187+
{'name': 'pkid', 'type': 'int', 'required': False},
188+
{'name': 'name', 'type': 'str', 'required': False},
189+
{'name': 'username', 'type': 'str', 'required': False},
190+
{'name': 'organization', 'type': 'int', 'required': False}]
191+
192+
def on_request(self, **kwargs):
161193
"""Celery task to get one or more existing users.
162194
163195
Args:
@@ -167,7 +199,7 @@ def on_request(self, auth, **kwargs):
167199
A return_dict dictionary containing the results of the API call. See
168200
get_empty_return_dict for more information.
169201
"""
170-
authorize_access(self.database_connection, auth, self.config)
202+
authorize_access(self.database_connection, self.auth, self.config)
171203
return model_get(User, self.database_connection, **kwargs)
172204

173205
def endpoint_source():

0 commit comments

Comments
 (0)