summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xdocs/core/core_api_contract2
-rw-r--r--docs/core/index.rst228
-rw-r--r--src/leap/bitmask/__init__.py6
-rwxr-xr-xsrc/leap/bitmask/cli/bitmask_cli.py16
-rw-r--r--src/leap/bitmask/core/dispatcher.py57
-rw-r--r--src/leap/bitmask/core/dummy.py56
-rw-r--r--src/leap/bitmask/core/launcher.py1
-rw-r--r--src/leap/bitmask/core/service.py1
-rw-r--r--src/leap/bitmask/core/web/_auth.py3
-rw-r--r--src/leap/bitmask/core/web/service.py8
-rw-r--r--tests/unit/core/test_web_api.py142
11 files changed, 428 insertions, 92 deletions
diff --git a/docs/core/core_api_contract b/docs/core/core_api_contract
index b70fb8f..ba4a963 100755
--- a/docs/core/core_api_contract
+++ b/docs/core/core_api_contract
@@ -21,6 +21,8 @@ api for Bitmask Core.
The values are meant to be type annotations.
"""
+import pkg_resources
+pkg_resources.get_distribution('leap.bitmask')
if __name__ == "__main__":
from leap.bitmask.core.service import BitmaskBackend
diff --git a/docs/core/index.rst b/docs/core/index.rst
index c52dcc1..9740744 100644
--- a/docs/core/index.rst
+++ b/docs/core/index.rst
@@ -4,10 +4,230 @@
.. _bitmask_core:
+============
Bitmask Core
-================================
-blah blah
+============
-API documentation
---------------------------------
+The bitmask core daemon can be launched like this::
+ bitmaskd
+
+The command-line program, ``bitmaskctl``, and the GUI, will launch the
+daemon when needed.
+
+Starting the API server
+=======================
+
+If configured to do so, the bitmask core will expose all of the commands
+throught a REST API. In bitmaskd.cfg::
+
+ [services]
+ web = True
+
+
+Resources
+=========
+
+Following is a list of currently available resources and a brief description of
+each one. For details click on the resource name.
+
++-----------------------------------+---------------------------------+
+| Resource | Description |
++===================================+=================================+
+| ``POST`` :ref:`cmd_core_version` | Get Bitmask Core Version Info |
++-----------------------------------+---------------------------------+
+| ``POST`` :ref:`cmd_core_stats` | Get Stats about Bitmask Usage |
++-----------------------------------+---------------------------------+
+| ``POST`` :ref:`cmd_core_status` | Get Bitmask Status |
++-----------------------------------+---------------------------------+
+| ``POST`` :ref:`cmd_core_stop` | Stop Bitmask Core |
++-----------------------------------+---------------------------------+
+| ``POST`` :ref:`cmd_prov_list` | List all providers |
++-----------------------------------+---------------------------------+
+| ``POST`` :ref:`cmd_prov_create` | Create a new provider |
++-----------------------------------+---------------------------------+
+| ``POST`` :ref:`cmd_prov_read` | Get info about a provider |
++-----------------------------------+---------------------------------+
+| ``POST`` :ref:`cmd_prov_del` | Delete a given provider |
++-----------------------------------+---------------------------------+
+| ``POST`` :ref:`cmd_user_list` | List all users |
++-----------------------------------+---------------------------------+
+| ``POST`` :ref:`cmd_user_active` | Get active user |
++-----------------------------------+---------------------------------+
+| ``POST`` :ref:`cmd_user_create` | Create a new user |
++-----------------------------------+---------------------------------+
+| ``POST`` :ref:`cmd_user_update` | Update an user |
++-----------------------------------+---------------------------------+
+| ``POST`` :ref:`cmd_user_auth` | Authenticate an user |
++-----------------------------------+---------------------------------+
+| ``POST`` :ref:`cmd_user_logout` | End session for an user |
++-----------------------------------+---------------------------------+
+| ``POST`` :ref:`cmd_keys_list` | Get all known keys for an user |
++-----------------------------------+---------------------------------+
+| ``POST`` :ref:`cmd_keys_insert` | Insert a new key |
++-----------------------------------+---------------------------------+
+| ``POST`` :ref:`cmd_keys_del` | Delete a given key |
++-----------------------------------+---------------------------------+
+| ``POST`` :ref:`cmd_keys_export` | Export keys |
++-----------------------------------+---------------------------------+
+
+.. _cmd_core_version:
+
+/core/version
+-------------
+**POST /core/version**
+
+ Get Bitmask Core Version Info
+
+.. _cmd_core_stats:
+
+/core/stats
+-----------
+**POST /core/stats**
+
+ Get Stats about Bitmask Usage
+
+.. _cmd_core_status:
+
+/core/status
+------------
+**POST /core/status**
+
+ Get Bitmask status
+
+.. _cmd_core_stop:
+
+/core/stop
+----------
+**POST /core/stop**
+
+ Stop Bitmask core (daemon shutdown).
+
+.. _cmd_prov_list:
+
+/bonafide/provider/list
+-----------------------
+**POST /bonafide/provider/list**
+
+ List all known providers.
+
+.. _cmd_prov_create:
+
+/bonafide/provider/create
+--------------------------
+**POST /bonafide/provider**
+
+ Create a new provider.
+
+.. _cmd_prov_read:
+
+/bonafide/provider/read
+-----------------------
+**POST /bonafide/provider/read**
+
+ Get info bout a given provider.
+
+.. _cmd_prov_del:
+
+/bonafide/provider/delete
+-------------------------
+**POST /bonafide/provider/delete**
+
+ Delete a given provider.
+
+
+.. _cmd_user_list:
+
+/bonafide/user/list
+-------------------
+**POST /bonafide/user/list**
+
+ List all the users known to the local backend.
+
+ **Form parameters**:
+ * ``foo`` *(required)* - foo bar.
+ * ``bar`` *(optional)* - foo bar.
+
+ **Status codes**:
+ * ``200`` - no error
+
+.. _cmd_user_active:
+
+/bonafide/user/active
+---------------------
+**POST /bonafide/user/active**
+
+ Get the active user.
+
+.. _cmd_user_create:
+
+/bonafide/user/create
+---------------------
+**POST /bonafide/user/create**
+
+ Create a new user.
+
+ **Form parameters**:
+ * ``foo`` *(required)* - foo bar.
+
+.. _cmd_user_update:
+
+/bonafide/user/update
+---------------------
+**POST /bonafide/user/update**
+
+ Update a given user.
+
+.. _cmd_user_auth:
+
+/bonafide/user/authenticate
+---------------------------
+**POST /bonafide/user/authenticate**
+
+ Authenticate an user.
+
+.. _cmd_user_logout:
+
+/bonafide/user/logout
+---------------------
+**POST /bonafide/user/logout**
+
+ Logs out an user, and destroys its local session.
+
+.. _cmd_keys_list:
+
+/keys/list
+-------------------
+**POST /keys/list**
+
+ Get all keys for an user.
+
+.. _cmd_keys_insert:
+
+/keys/insert/
+-------------------
+**POST /keys/insert**
+
+ Insert a new key for an user.
+
+.. _cmd_keys_del:
+
+/keys/delete/
+-------------------
+**POST /keys/delete**
+
+ Delete a key for an user.
+
+.. _cmd_keys_export:
+
+/keys/export/
+-------------------
+**POST /keys/export**
+
+ Export keys for an user.
+
+
+API Authentication
+==================
+
+(TBD) Most of the resources in the API are protected by an authentication token.
diff --git a/src/leap/bitmask/__init__.py b/src/leap/bitmask/__init__.py
index 20719d4..6fd6174 100644
--- a/src/leap/bitmask/__init__.py
+++ b/src/leap/bitmask/__init__.py
@@ -2,9 +2,6 @@ import sys
import pkg_resources
from ._version import get_versions
-__version__ = get_versions()['version']
-del get_versions
-
if not getattr(sys, 'frozen', False):
# FIXME: HACK for https://github.com/pypa/pip/issues/3
# Without this 'fix', there are resolution conflicts when pip installs at
@@ -12,3 +9,6 @@ if not getattr(sys, 'frozen', False):
# namespace from pypi. For instance:
# 'pip install -e .' and 'pip install leap.common'
pkg_resources.get_distribution('leap.bitmask')
+
+__version__ = get_versions()['version']
+del get_versions
diff --git a/src/leap/bitmask/cli/bitmask_cli.py b/src/leap/bitmask/cli/bitmask_cli.py
index 893b7d1..dfd1fbc 100755
--- a/src/leap/bitmask/cli/bitmask_cli.py
+++ b/src/leap/bitmask/cli/bitmask_cli.py
@@ -60,7 +60,6 @@ GENERAL COMMANDS:
'''
epilog = ("Use 'bitmaskctl <command> help' to learn more "
"about each command.")
- commands = ['stop', 'stats']
def user(self, raw_args):
user = User()
@@ -99,7 +98,7 @@ GENERAL COMMANDS:
return defer.succeed(None)
def version(self, raw_args):
- self.data = ['version']
+ self.data = ['core', 'version']
return self._send(printer=self._print_version)
def _print_version(self, version):
@@ -107,7 +106,7 @@ GENERAL COMMANDS:
print(Fore.GREEN + 'bitmask_core: ' + Fore.RESET + corever)
def status(self, raw_args):
- self.data = ['status']
+ self.data = ['core', 'status']
return self._send(printer=self._print_status)
def _print_status(self, status):
@@ -119,11 +118,19 @@ GENERAL COMMANDS:
print(key.ljust(10) + ': ' + color +
value + Fore.RESET)
+ def stop(self, raw_args):
+ self.data = ['core', 'stop']
+ return self._send(printer=command.default_dict_printer)
+
+ def stats(self, raw_args):
+ self.data = ['core', 'stats']
+ return self._send(printer=command.default_dict_printer)
+
@defer.inlineCallbacks
def execute():
cli = BitmaskCLI()
- cli.data = ['version']
+ cli.data = ['core', 'version']
args = ['--verbose'] if '--verbose' in sys.argv else None
yield cli._send(
timeout=0.1, printer=_null_printer,
@@ -152,5 +159,6 @@ def main():
signal.signal(signal.SIGINT, signal_handler)
reactor.run()
+
if __name__ == "__main__":
main()
diff --git a/src/leap/bitmask/core/dispatcher.py b/src/leap/bitmask/core/dispatcher.py
index 5900390..b068683 100644
--- a/src/leap/bitmask/core/dispatcher.py
+++ b/src/leap/bitmask/core/dispatcher.py
@@ -38,6 +38,10 @@ from .api import APICommand, register_method
logger = Logger()
+class DispatchError(Exception):
+ pass
+
+
class SubCommand(object):
__metaclass__ = APICommand
@@ -112,6 +116,8 @@ class UserCmd(SubCommand):
@register_method("{'signup': 'ok', 'user': str}")
def do_CREATE(self, bonafide, *parts):
+ if len(parts) < 5:
+ raise DispatchError('Not enough parameters passed')
# params are: [user, create, full_id, password, invite, autoconf]
user, password, invite = parts[2], parts[3], parts[4]
@@ -219,7 +225,6 @@ class WebUICmd(SubCommand):
@register_method('dict')
def do_STATUS(self, webui, *parts, **kw):
- print 'webui', webui
d = webui.do_status()
return d
@@ -310,7 +315,6 @@ class EventsCmd(SubCommand):
self.waiting.append(d)
return d
- @register_method("")
def _callback(self, event, *content):
payload = (str(event), content)
if not self.waiting:
@@ -322,15 +326,35 @@ class EventsCmd(SubCommand):
d.callback(payload)
+class CoreCmd(SubCommand):
+
+ label = 'core'
+
+ @register_method("{'mem_usage': str}")
+ def do_STATS(self, core, *parts):
+ return core.do_stats()
+
+ @register_method("{version_core': '0.0.0'}")
+ def do_VERSION(self, core, *parts):
+ return core.do_version()
+
+ @register_method("{'mail': 'running'}")
+ def do_STATUS(self, core, *parts):
+ return core.do_status()
+
+ @register_method("{'stop': 'ok'}")
+ def do_STOP(self, core, *parts):
+ return core.do_stop()
+
+
class CommandDispatcher(object):
__metaclass__ = APICommand
- label = 'core'
-
def __init__(self, core):
self.core = core
+ self.subcommand_core = CoreCmd()
self.subcommand_bonafide = BonafideCmd()
self.subcommand_eip = EIPCmd()
self.subcommand_mail = MailCmd()
@@ -338,26 +362,10 @@ class CommandDispatcher(object):
self.subcommand_events = EventsCmd()
self.subcommand_webui = WebUICmd()
- # XXX --------------------------------------------
- # TODO move general services to another subclass
-
- @register_method("{'mem_usage': str}")
- def do_STATS(self, *parts):
- return _format_result(self.core.do_stats())
-
- @register_method("{version_core': '0.0.0'}")
- def do_VERSION(self, *parts):
- return _format_result(self.core.do_version())
-
- @register_method("{'mail': 'running'}")
- def do_STATUS(self, *parts):
- return _format_result(self.core.do_status())
-
- @register_method("{'stop': 'ok'}")
- def do_STOP(self, *parts):
- return _format_result(self.core.do_stop())
-
- # -----------------------------------------------
+ def do_CORE(self, *parts):
+ d = self.subcommand_core.dispatch(self.core, *parts)
+ d.addCallbacks(_format_result, _format_error)
+ return d
def do_BONAFIDE(self, *parts):
bonafide = self._get_service('bonafide')
@@ -446,7 +454,6 @@ class CommandDispatcher(object):
def dispatch(self, msg):
cmd = msg[0]
-
_method = getattr(self, 'do_' + cmd.upper(), None)
if not _method:
diff --git a/src/leap/bitmask/core/dummy.py b/src/leap/bitmask/core/dummy.py
index 455756c..2037b81 100644
--- a/src/leap/bitmask/core/dummy.py
+++ b/src/leap/bitmask/core/dummy.py
@@ -22,6 +22,34 @@ import json
from leap.bitmask.hooks import HookableService
+class CannedData:
+
+ class backend:
+ status = {
+ 'soledad': 'running',
+ 'keymanager': 'running',
+ 'mail': 'running',
+ 'eip': 'stopped',
+ 'backend': 'dummy'}
+ version = {'version_core': '0.0.1'}
+ stop = {'stop': 'ok'}
+ stats = {'mem_usage': '01 KB'}
+
+ class bonafide:
+ auth = {
+ u'srp_token': u'deadbeef123456789012345678901234567890123',
+ u'uuid': u'01234567890abcde01234567890abcde'}
+ signup = {
+ 'signup': 'ok',
+ 'user': 'dummyuser@provider.example.org'}
+ list_users = {
+ 'userid': 'testuser',
+ 'authenticated': False}
+ logout = {
+ 'logout': 'ok'}
+ get_active_user = 'dummyuser@provider.example.org'
+
+
class BackendCommands(object):
"""
@@ -30,23 +58,19 @@ class BackendCommands(object):
def __init__(self, core):
self.core = core
+ self.canned = CannedData
def do_status(self):
- return json.dumps(
- {'soledad': 'running',
- 'keymanager': 'running',
- 'mail': 'running',
- 'eip': 'stopped',
- 'backend': 'dummy'})
+ return json.dumps(self.canned.backend.stats)
def do_version(self):
- return {'version_core': '0.0.1'}
+ return self.canned.backend.version
def do_stats(self):
- return {'mem_usage': '01 KB'}
+ return self.canned.backend.stats
def do_stop(self):
- return {'stop': 'ok'}
+ return self.canned.backend.stop
class mail_services(object):
@@ -64,17 +88,19 @@ class mail_services(object):
class BonafideService(HookableService):
def __init__(self, basedir):
- pass
+ self.canned = CannedData
def do_authenticate(self, user, password):
- return {u'srp_token': u'deadbeef123456789012345678901234567890123',
- u'uuid': u'01234567890abcde01234567890abcde'}
+ return self.canned.bonafide.auth
def do_signup(self, user, password):
- return {'signup': 'ok', 'user': 'dummyuser@provider.example.org'}
+ return self.canned.bonafide.signup
+
+ def do_list_users(self):
+ return self.canned.bonafide.list_users
def do_logout(self, user):
- return {'logout': 'ok'}
+ return self.canned.bonafide.logout
def do_get_active_user(self):
- return 'dummyuser@provider.example.org'
+ return self.canned.bonafide.get_active_user
diff --git a/src/leap/bitmask/core/launcher.py b/src/leap/bitmask/core/launcher.py
index a1c8690..14d8e60 100644
--- a/src/leap/bitmask/core/launcher.py
+++ b/src/leap/bitmask/core/launcher.py
@@ -45,6 +45,7 @@ def here(module=None):
def run_bitmaskd():
+
# TODO --- configure where to put the logs... (get --logfile, --logdir
# from bitmaskctl
for (index, arg) in enumerate(sys.argv):
diff --git a/src/leap/bitmask/core/service.py b/src/leap/bitmask/core/service.py
index 9682c18..c3e97f7 100644
--- a/src/leap/bitmask/core/service.py
+++ b/src/leap/bitmask/core/service.py
@@ -266,6 +266,7 @@ class BackendCommands(object):
return {'version_core': __version__}
def do_stats(self):
+ print "DO STATS"
logger.debug('BitmaskCore Service STATS')
mem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
return {'mem_usage': '%s MB' % (mem / 1024)}
diff --git a/src/leap/bitmask/core/web/_auth.py b/src/leap/bitmask/core/web/_auth.py
index 6a5e362..3eb4fa1 100644
--- a/src/leap/bitmask/core/web/_auth.py
+++ b/src/leap/bitmask/core/web/_auth.py
@@ -18,9 +18,6 @@ class WhitelistHTTPAuthSessionWrapper(HTTPAuthSessionWrapper):
It doesn't apply the enforcement to routes included in a whitelist.
"""
- # TODO extend this to inspect the data -- so that we pass a tuple
- # with the action
-
whitelist = (None,)
def __init__(self, *args, **kw):
diff --git a/src/leap/bitmask/core/web/service.py b/src/leap/bitmask/core/web/service.py
index 2437d2d..77e1c72 100644
--- a/src/leap/bitmask/core/web/service.py
+++ b/src/leap/bitmask/core/web/service.py
@@ -59,7 +59,13 @@ class HTTPDispatcherService(service.Service):
"""
API_WHITELIST = (
- '/API/bonafide/user',
+ '/API/core/version',
+ '/API/core/stats',
+ '/API/bonafide/user/create',
+ '/API/bonafide/user/authenticate',
+ '/API/bonafide/provider/list',
+ '/API/bonafide/provider/create',
+ '/API/bonafide/provider/read',
)
def __init__(self, core, port=7070, debug=False, onion=False):
diff --git a/tests/unit/core/test_web_api.py b/tests/unit/core/test_web_api.py
index ae10ec4..02708f6 100644
--- a/tests/unit/core/test_web_api.py
+++ b/tests/unit/core/test_web_api.py
@@ -1,16 +1,22 @@
+import json
import base64
from twisted.application import service
from twisted.cred import portal
+from twisted.internet import defer, reactor
+from twisted.python.compat import networkString
from twisted.trial import unittest
-from twisted.web.test.test_web import DummyRequest
+from twisted.web import client
from twisted.web import resource
from twisted.web.server import Site
+from twisted.web.test.test_web import DummyRequest
from leap.bitmask.core import dispatcher
from leap.bitmask.core import web
from leap.bitmask.core.dummy import mail_services
from leap.bitmask.core.dummy import BonafideService
+from leap.bitmask.core.dummy import BackendCommands
+from leap.bitmask.core.dummy import CannedData
def b64encode(s):
@@ -102,47 +108,106 @@ class WhitelistedResourceTests(SimpleAPIMixin, unittest.TestCase):
pass
-class RESTApiMixin:
- def setUp(self):
- self.request = self.makeRequest()
+class AuthTestResource(resource.Resource):
- dispatcher = dummyDispatcherFactory()
- api = web.api.Api(dispatcher)
- self.site = Site(api)
+ isLeaf = True
- def makeRequest(self, method=b'GET', clientAddress=None):
- """
- Create a request object to be passed to
- TokenCredentialFactory.decode along with a response value.
- Override this in a subclass.
- """
- raise NotImplementedError("%r did not implement makeRequest" % (
- self.__class__,))
+ def render_GET(self, request):
+ return "dummyGET"
+
+ def render_POST(self, request):
+ return "dummyPOST"
-class RESTApiTests(RESTApiMixin, unittest.TestCase):
+class RESTApiTests(unittest.TestCase):
"""
Tests that involve checking the routing between the REST api and the
command dispatcher.
- """
- def test_simple_api_request(self):
- # FIXME -- check the requests to the API
- assert 1 == 1
+ This is just really testing the canned responses in the Dummy backend.
+ To make sure that those responses match the live data, e2e tests should be
+ run.
+ """
- def makeRequest(self, method='GET', clientAddress=None):
- pass
+ def setUp(self):
+ dispatcher = dummyDispatcherFactory()
+ api = web.api.Api(dispatcher)
+ plainSite = Site(api)
+ self.plainPort = reactor.listenTCP(0, plainSite, interface="127.0.0.1")
+ self.plainPortno = self.plainPort.getHost().port
+ self.canned = CannedData
+
+ def tearDown(self):
+ return self.plainPort.stopListening()
+
+ # core commands
+
+ @defer.inlineCallbacks
+ def test_core_version(self):
+ call = yield self.makeAPICall('core/version')
+ self.assertCall(call, self.canned.backend.version)
+
+ @defer.inlineCallbacks
+ def test_core_stop(self):
+ call = yield self.makeAPICall('core/stop')
+ self.assertCall(call, self.canned.backend.stop)
+
+ # bonafide commands
+
+ @defer.inlineCallbacks
+ def test_bonafide_user_list(self):
+ call = yield self.makeAPICall('bonafide/user/list')
+ self.assertCall(call, self.canned.bonafide.list_users)
+
+ @defer.inlineCallbacks
+ def test_bonafide_user_create(self):
+ call = yield self.makeAPICall('bonafide/user/create')
+ self.assertCall(call, self.canned.bonafide.auth)
+
+ @defer.inlineCallbacks
+ def test_bonafide_user_update(self):
+ call = yield self.makeAPICall('bonafide/user/update')
+ self.assertCall(call, self.canned.bonafide.update)
+
+ @defer.inlineCallbacks
+ def test_bonafide_user_authenticate(self):
+ call = yield self.makeAPICall('bonafide/user/authenticate')
+ self.assertCall(call, self.canned.bonafide.auth)
+
+ @defer.inlineCallbacks
+ def test_bonafide_user_active(self):
+ call = yield self.makeAPICall('bonafide/user/active')
+ self.assertCall(call, self.canned.bonafide.get_active_user)
+
+ @defer.inlineCallbacks
+ def test_bonafide_user_logout(self):
+ call = yield self.makeAPICall('bonafide/user/logout')
+ self.assertCall(call, self.canned.bonafide.logout)
+
+ def makeAPICall(self, path, method="POST"):
+ uri = networkString("http://127.0.0.1:%d/%s" % (
+ self.plainPortno, path))
+ return client.getPage(uri, method=method, timeout=1)
+
+ def assertCall(self, returned, expected):
+ data = json.loads(returned)
+ error = data['error']
+ assert error is None
+ result = data['result']
+ assert result == expected
class DummyCore(service.MultiService):
+
"""
A minimal core that uses the dummy backend modules.
"""
def __init__(self):
service.MultiService.__init__(self)
- mail = mail_services.StandardMailService
- self.init('mail', mail)
+
+ bf = BonafideService
+ self.init('bonafide', bf, '/tmp/')
km = mail_services.KeymanagerService
self.init('keymanager', km)
@@ -150,14 +215,28 @@ class DummyCore(service.MultiService):
sol = mail_services.SoledadService
self.init('soledad', sol)
- bf = BonafideService
- self.init('bonafide', bf, '/tmp/')
+ mail = mail_services.StandardMailService
+ self.init('mail', mail)
+
+ self.core_cmds = BackendCommands(self)
def init(self, label, service, *args, **kw):
s = service(*args, **kw)
s.setName(label)
s.setServiceParent(self)
+ def do_stats(self):
+ return self.core_cmds.do_stats()
+
+ def do_version(self):
+ return self.core_cmds.do_version()
+
+ def do_status(self):
+ return self.core_cmds.do_status()
+
+ def do_stop(self):
+ return self.core_cmds.do_stop()
+
def dummyDispatcherFactory():
"""
@@ -165,14 +244,3 @@ def dummyDispatcherFactory():
"""
dummy_core = DummyCore()
return dispatcher.CommandDispatcher(dummy_core)
-
-
-class AuthTestResource(resource.Resource):
-
- isLeaf = True
-
- def render_GET(self, request):
- return "dummyGET"
-
- def render_POST(self, request):
- return "dummyPOST"