Exemplo n.º 1
0
def generate_cookie(key, value, expires=None, path='/', domain=None,
                    max_age=None, secure=None, httponly=False):
    """Generate a cookie. Called by the function of set_cookie(). The params are
    the same as the Morsel object in the Cookie which is a Python Standard Library.

    :param key: the name of the cookie to be set
    :param value: the value of the cookie
    :param expires: should be a `datetime` object or UNIX timestamp.
    :param path: limit the cookie to the given path.
    :param domain: set a cross-domain cookie. Example:
                   domain=".example.com", it will set the cookie that is
                   readable by the domain "www.example.com"
    :param max_age: should be a number of seconds or None.
    :param secure: The cookie will only be available via HTTPS

    :return morsel: Type is Morsel.
    """

    morsel = Morsel()
    if key is not None:
        morsel.set(key, value, value)

    if expires is not None:
        if not isinstance(expires, basestring):
            expires = set_cookie_date(expires)
        morsel['expires'] = expires
    elif max_age is not None:
        morsel['expires'] = set_cookie_date(time() + max_age)

    for key, value in (('path', path), ('domain', domain),
                       ('secure', secure), ('httponly', httponly)):
        if value is not None and value is not False:
            morsel[key] = value

    return morsel
Exemplo n.º 2
0
 def setcookie(self,
               key,
               value,
               max_age=None,
               expires=None,
               path='/',
               domain=None,
               secure=None,
               httponly=False):
     newcookie = Morsel()
     newcookie.key = key
     newcookie.value = value
     newcookie.coded_value = value
     if max_age is not None:
         newcookie['max-age'] = max_age
     if expires is not None:
         newcookie['expires'] = expires
     if path is not None:
         newcookie['path'] = path
     if domain is not None:
         newcookie['domain'] = domain
     if secure:
         newcookie['secure'] = secure
     if httponly:
         newcookie['httponly'] = httponly
     self.sent_cookies = [c for c in self.sent_cookies if c.key != key]
     self.sent_cookies.append(newcookie)
Exemplo n.º 3
0
    def setUp(self):
        """Setting up test."""
        self.logd("setUp")
        self.server_url = self.conf_get('main', 'url')
        # XXX here you can setup the credential access like this
        credential_host = self.conf_get('credential', 'host')
        credential_port = self.conf_getInt('credential', 'port')
        self.login, self.password = xmlrpc_get_credential(credential_host,
                                                           credential_port,
                                                           'members')
        #self.login = '******'
        #self.password = '******'

        # Set the zope cookie. This is a little evil but avoids having to
        # call the login page.
        morsel = Morsel()
        morsel.key = '__ac'
        morsel.value = morsel.coded_value = urlquote(
            encodestring('%s:%s' % (self.login, self.password)))
        self._browser.cookies = {
            'rhaptos': {
                '/': {
                    '__ac': morsel
                }
            }
        }
Exemplo n.º 4
0
    def setUp(self):
        """Setting up test, in current case just setting the server url."""
        self.logd("setUp")
        self.label = "Authenticated Tests (QAauth)"
        self.server_url = self.conf_get('main', 'url')
        hostname = urlparse(self.server_url)[1].split(':')[0]
        credential_host = self.conf_get('credential', 'host')
        credential_port = self.conf_getInt('credential', 'port')

        self.login, self.password = xmlrpc_get_credential(credential_host,
                                                           credential_port,
                                                           'members')

        # Set the zope cookie. This is a little evil but avoids having to
        # call the login page.
        morsel = Morsel()
        morsel.key = '__ac'
        morsel.value = morsel.coded_value = urlquote(
            encodestring('%s:%s' % (self.login, self.password)))
        self._browser.cookies = {
            hostname: {
                '/': {
                    '__ac': morsel
                }
            }
        }
Exemplo n.º 5
0
 def test_get_user_from_token(self):
     instance = ESAPI.authenticator()
     instance.logout()
     
     account_name = "testUserFromToken"
     password = instance.generate_strong_password()
     user = instance.create_user(account_name, password, password)
     user.enable()
     
     ###
     request = MockHttpRequest()
     response = MockHttpResponse()
     ESAPI.http_utilities().set_current_http(request, response)
     
     m = Morsel()
     m.key = HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME
     m.value = "ridiculous"
     request.cookies[m.key] = m
     # Wrong cookie should fail
     self.assertRaises(AuthenticationException, instance.login, request, response)
     user.logout()
     ###
     
     request = MockHttpRequest()
     response = MockHttpResponse()
     ESAPI.authenticator().current_user = user
     new_token = ESAPI.http_utilities().set_remember_token(
         password, 10000, "test.com", request.path, request, response )
     request.set_cookie( key=HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME, value=new_token )
     ESAPI.http_utilities().set_current_http(request, response)
     
     # Logout the current user so we can log them in with the remember cookie
     user2 = instance.login(request, response)
     self.assertEquals(user, user2)
Exemplo n.º 6
0
 def to_cookies(self):
     
     for key, (value, deleted) in self.__data.iteritems():
         
         morsel = Morsel()
         morsel.key = "session__%s" % key
         
         if not deleted:
             
             value = pickle.dumps(value)
             value = base64.b64encode(value)
             sig = quick_hash(self.id, self.secret_key, value)
             value = ".".join((sig, value))
             
         else:
             value = 'no-value'
             morsel['expires'] = -3600
             # Sets the cookie to expire in the past.
         
         
         morsel.coded_value = morsel.value = value
         
         # This optionally sets the cookie's timeout till
         # deletion.
         if 'timeout' in self.options:
             timeout = self.options['timeout']
             morsel['expires'] = timeout
             morsel['max-age'] = timeout
             
         morsel['path'] = '/'
         
         yield morsel
Exemplo n.º 7
0
Arquivo: http.py Projeto: rivan/w3fu
 def set_cookie(self, name, value, expires=None, path='/'):
     morsel = Morsel()
     morsel.set(name, value, str(value))
     morsel['path'] = path
     if expires is not None:
         morsel['expires'] = expires.strftime('%a, %d %b %Y %H:%M:%S GMT')
     self.header('Set-Cookie', morsel.OutputString())
     return self
Exemplo n.º 8
0
class TestExpiredCookie(TestCase):

    def setUp(self):
        self.cookie = Morsel()
        self.cookie.set('foo', 'bar', 'bar')
        self.cookie.time_received = time.time() - 1

    def test_no_expiration_information(self):
        assert not is_cookie_expired(self.cookie)

    def test_valid_max_age(self):
        self.cookie['max-age'] = '86400'
        assert not is_cookie_expired(self.cookie)

    def test_valid_max_age_missing_time_received(self):
        self.cookie['max-age'] = '1'
        del self.cookie.time_received
        assert not is_cookie_expired(self.cookie)

    def test_expired_max_age(self):
        self.cookie['max-age'] = '1'
        assert is_cookie_expired(self.cookie)
        self.cookie['max-age'] = '0'
        assert is_cookie_expired(self.cookie)

    def test_expired_max_age_missing_time_received(self):
        self.cookie['max-age'] = '0'
        del self.cookie.time_received
        assert is_cookie_expired(self.cookie)
        self.cookie['max-age'] = '1'
        assert not is_cookie_expired(self.cookie)

    def test_invalid_max_age(self):
        self.cookie['max-age'] = 'invalid'
        assert not is_cookie_expired(self.cookie)

    def test_valid_expires(self):
        self.cookie['expires'] = formatdate(time.time() + 86400, localtime=True)
        assert not is_cookie_expired(self.cookie)

    def test_expired_expires(self):
        self.cookie['expires'] = formatdate(usegmt=True)
        assert is_cookie_expired(self.cookie)

    def test_invalid_expires(self):
        self.cookie['expires'] = 'invalid'
        assert not is_cookie_expired(self.cookie)

    def test_valid_max_age_and_expired_expires(self):
        self.cookie['max-age'] = '86400'
        self.cookie['expires'] = formatdate(usegmt=True)
        assert not is_cookie_expired(self.cookie)

    def test_expired_max_age_and_valid_expires(self):
        self.cookie['max-age'] = '1'
        self.cookie['expires'] = formatdate(time.time() + 86400, localtime=True)
        assert is_cookie_expired(self.cookie)
Exemplo n.º 9
0
 def set_cookie(self, **kwargs):
     key = kwargs['key']
     m = Morsel()
     m.key = key
     m.value = kwargs['value']
     m.coded_value = kwargs['value']
     for k, v in kwargs.items():
         try:
             m[k] = v
         except:
             pass
     self.cookies[key] = m
     self.headers['Set-Cookie'] = str(m)
Exemplo n.º 10
0
 def set_cookie(self, **kwargs):
     key = kwargs['key']
     m = Morsel()
     m.key = key
     m.value = kwargs['value']
     m.coded_value = kwargs['value']
     for k, v in kwargs.items():
         try:
             m[k] = v
         except:
             pass
     self.cookies[key] = m
     self.headers['Set-Cookie'] = str(m)
Exemplo n.º 11
0
 def set_cookie(self, key, value=None, path='/', domain=None, secure=False, httponly=False, expires=None):
     morsel = Morsel()
     morsel.key = key
     morsel.coded_value = value
     morsel['path'] = path
     if expires:
         morsel['expires'] = expires
     if domain:
         morsel['domain'] = domain
     if secure:
         morsel['secure'] = secure
     if httponly:
         morsel['httponly'] = httponly
     self.append_response_header('Set-Cookie', morsel.OutputString())
Exemplo n.º 12
0
 def injecting_start_response(status, headers, exc_info=None):
     if session.should_save:
         morsel = Morsel()
         morsel.key = self.cookie_name
         morsel.coded_value = session.sid
         if self.cookie_age is not None:
             morsel['max-age'] = self.cookie_age
             morsel['expires'] = cookie_date(time() + self.cookie_age)
         if self.cookie_domain is not None:
             morsel['domain'] = self.cookie_domain
         if self.cookie_path is not None:
             morsel['path'] = self.cookie_path
         if self.cookie_secure is not None:
             morsel['secure'] = self.cookie_secure
         headers.append(tuple(str(morsel).split(':', 1)))
     return start_response(status, headers, exc_info)
Exemplo n.º 13
0
 def delete_cookie(self, key, path='/', domain=None):
     """
     Deletes a cookie from the client by setting the cookie to an empty
     string, and max_age=0 so it should expire immediately.
     """
     if self.cookies.has_key(key):
         self.cookies[key].value = ''
         self.cookies[key]['max-age'] = 0
     else:
         m = Morsel()
         m.key = key
         m.value = ''
         m.path = path
         m.domain = domain
         m['max-age'] = 0
         self.cookies[key] = m
Exemplo n.º 14
0
 def start(self, cookies, cookieopts=None):
     c = SimpleCookie(cookies)
     sid = c.get(self.cookiename)
     create = True
     if sid is not None:
         for m in self.get(sid.value):
             yield m
         if self.apiroutine.retvalue is not None:
             self.apiroutine.retvalue = (self.SessionHandle(
                 self.apiroutine.retvalue, self.apiroutine), [])
             create = False
     if create:
         for m in self.create():
             yield m
         sh = self.apiroutine.retvalue
         m = Morsel()
         m.key = self.cookiename
         m.value = sh.id
         m.coded_value = sh.id
         opts = {'path': '/', 'httponly': True}
         if cookieopts:
             opts.update(cookieopts)
             if not cookieopts['httponly']:
                 del cookieopts['httponly']
         m.update(opts)
         self.apiroutine.retvalue = (sh, [m])
Exemplo n.º 15
0
 async def start(self, cookies, cookieopts=None):
     """
     Session start operation. First check among the cookies to find existed sessions;
     if there is not an existed session, create a new one.
     
     :param cookies: cookie header from the client
     
     :param cookieopts: extra options used when creating a new cookie
     
     :return: ``(session_handle, cookies)`` where session_handle is a SessionHandle object,
              and cookies is a list of created Set-Cookie headers (may be empty)
     """
     c = SimpleCookie(cookies)
     sid = c.get(self.cookiename)
     create = True
     if sid is not None:
         sh = await self.get(sid.value)
         if sh is not None:
             return (self.SessionHandle(sh, self.apiroutine), [])
     if create:
         sh = await self.create()
         m = Morsel()
         m.key = self.cookiename
         m.value = sh.id
         m.coded_value = sh.id
         opts = {'path': '/', 'httponly': True}
         if cookieopts:
             opts.update(cookieopts)
             if not cookieopts['httponly']:
                 del cookieopts['httponly']
         m.update(opts)
         return (sh, [m])
Exemplo n.º 16
0
    def set_cookie(self, name, value, path=None, comment=None, domain=None,
                   max_age=None, secure=None, version=None, httponly=None):
        
        new_morsel = Morsel()
        new_morsel.key = name
        new_morsel.coded_value = new_morsel.value = value
        
        if max_age:
            # Morsels will take max age and convert it to a date for us.
            new_morsel['expires'] = max_age
            new_morsel['max-age'] = max_age

        
        if path:     new_morsel['path']     = path
        if comment:  new_morsel['comment']  = comment
        if domain:   new_morsel['domain']   = domain
        if secure:   new_morsel['secure']   = secure
        if version:  new_morsel['version']  = version
        if httponly: new_morsel['httponly'] = httponly
        
        self += new_morsel
Exemplo n.º 17
0
 def start(self, cookies, cookieopts=None):
     c = SimpleCookie(cookies)
     sid = c.get(self.cookiename)
     create = True
     if sid is not None:
         for m in self.get(sid.value):
             yield m
         if self.apiroutine.retvalue is not None:
             self.apiroutine.retvalue = (self.SessionHandle(self.apiroutine.retvalue, self.apiroutine), [])
             create = False
     if create:
         for m in self.create():
             yield m
         sh = self.apiroutine.retvalue
         m = Morsel()
         m.key = self.cookiename
         m.value = sh.id
         m.coded_value = sh.id
         opts = {"path": "/", "httponly": True}
         if cookieopts:
             opts.update(cookieopts)
             if not cookieopts["httponly"]:
                 del cookieopts["httponly"]
         m.update(opts)
         self.apiroutine.retvalue = (sh, [m])
Exemplo n.º 18
0
 def destroy(self, sessionid):
     for m in callAPI(self.apiroutine, 'memorystorage', 'delete',
                      {'key': __name__ + '.' + sessionid}):
         yield m
     m = Morsel()
     m.key = self.cookiename
     m.value = 'deleted'
     m.coded_value = 'deleted'
     opts = {'path': '/', 'httponly': True, 'max-age': 0}
     m.update(opts)
     self.apiroutine.retvalue = [m]
Exemplo n.º 19
0
    def test_morsel_to_cookie(self):
        from time import strftime, localtime
        time_template = "%a, %d-%b-%Y %H:%M:%S GMT"
        m = Morsel()
        m['domain'] = ".yandex"
        m['domain'] = ".yandex.ru"
        m['path'] = "/"
        m['expires'] = "Fri, 27-Aug-2021 17:43:25 GMT"
        m.key = "dj2enbdj3w"
        m.value = "fvjlrwnlkjnf"

        c = morsel_to_cookie(m)
        self.assertEquals(m.key, c.name)
        self.assertEquals(m.value, c.value)
        for x in ('expires', 'path', 'comment', 'domain',
                  'secure', 'version'):
            if x == 'expires':
                self.assertEquals(m[x], strftime(time_template, localtime(getattr(c, x, None))))
            elif x == 'version':
                self.assertTrue(isinstance(getattr(c, x, None), int))
            else:
                self.assertEquals(m[x], getattr(c, x, None))
Exemplo n.º 20
0
 def destroy(self, sessionid):
     for m in callAPI(self.apiroutine, "memorystorage", "delete", {"key": __name__ + "." + sessionid}):
         yield m
     m = Morsel()
     m.key = self.cookiename
     m.value = "deleted"
     m.coded_value = "deleted"
     opts = {"path": "/", "httponly": True, "max-age": 0}
     m.update(opts)
     self.apiroutine.retvalue = [m]
Exemplo n.º 21
0
 async def destroy(self, sessionid):
     """
     Destroy a session
     
     :param sessionid: session ID
     
     :return: a list of Set-Cookie headers to be sent to the client
     """
     await call_api(self.apiroutine, 'memorystorage', 'delete',
                    {'key': __name__ + '.' + sessionid})
     m = Morsel()
     m.key = self.cookiename
     m.value = 'deleted'
     m.coded_value = 'deleted'
     opts = {'path': '/', 'httponly': True, 'max-age': 0}
     m.update(opts)
     return [m]
Exemplo n.º 22
0
 def test_kill_cookie(self):
     request = MockHttpRequest()
     response = MockHttpResponse()
     
     ESAPI.http_utilities().set_current_http(request, response)
     self.assertTrue(len(response.cookies) == 0)
     
     new_cookies = {}
     m = Morsel()
     m.key = 'test1'
     m.value = '1'
     new_cookies[m.key] = m
     
     m = Morsel()
     m.key = 'test2'
     m.value = '2'
     new_cookies[m.key] = m
     
     request.cookies = new_cookies
     ESAPI.http_utilities().kill_cookie( "test1", request, response )
     self.assertTrue(len(response.cookies) == 1)
Exemplo n.º 23
0
 def delete_cookie(self, key, path='/', domain=None):
     """
     Deletes a cookie from the client by setting the cookie to an empty
     string, and max_age=0 so it should expire immediately.
     """
     if self.cookies.has_key(key):
         self.cookies[key].value = ''
         self.cookies[key]['max-age'] = 0
     else:
         m = Morsel()
         m.key = key
         m.value = ''
         m.path = path
         m.domain = domain
         m['max-age'] = 0
         self.cookies[key] = m
Exemplo n.º 24
0
 def OutputString(self, attrs = None):
     httponly = self.pop('httponly', False)
     result = Morsel.OutputString(self, attrs).rstrip('\t ;')
     if httponly:
         result += '; HttpOnly'
     return result
Exemplo n.º 25
0
 def __init__(self, name = None, value = None):
     Morsel.__init__(self)
     if name is not None:
         self.set(name, value, value)
Exemplo n.º 26
0
 def __init__(self, name=None, value=None):
     Morsel.__init__(self)
     if name is not None:
         self.set(name, value, value)
Exemplo n.º 27
0
 def __set(self, key, real_value, coded_value):
     """Private method for setting a cookie's value"""
     M = self.get(key, Morsel())
     M.set(key, real_value, coded_value, LegalChars=_LegalChars + "[]")
     dict.__setitem__(self, key, M)
Exemplo n.º 28
0
 def setUp(self):
     self.cookie = Morsel()
     self.cookie.set('foo', 'bar', 'bar')
     self.cookie.time_received = time.time() - 1
Exemplo n.º 29
0
    def submit_app(self):
        # try to submit an app
        ret = self.get('/developers/submit/app', ok_codes=[200, 302])

        # generate a random web-app manifest
        random_id = uuid.uuid1().hex
        manifest_url = WEBAPP % random_id

        # we need to accept the TOS once per user
        if 'read_dev_agreement' in ret.body:
            params = [['read_dev_agreement', 'True']]
            add_csrf_token(ret, params)
            ret = self.post(ret.url, params=params)

        # submit the manifest
        params = [['manifest', manifest_url]]
        add_csrf_token(ret, params)
        ret = self.post('/developers/upload-manifest', params=params)
        data = json.loads(ret.body)
        validation = data['validation']
        app_exists = False

        if isinstance(validation, dict) and 'messages' in validation:
            messages = [m['message'] for m in data['validation']['messages']]
            app_exists = 'already' in ' '.join(messages)

        # we should be able to test editing the app
        if app_exists:
            return

        if isinstance(validation, dict) and 'errors' in validation:
            self.assertEqual(data['validation']['errors'], 0, data)

        # now we can submit the app basics, first load the form again
        ret = self.get('/developers/submit/app/manifest')
        params = [['upload', data['upload']],
                  ['free', 'free-os'],
                  ['free', 'free-desktop'],
                  ['free', 'free-phone'],
                  ['free', 'free-tablet']]
        add_csrf_token(ret, params)
        ret = self.post('/developers/submit/app/manifest', params=params)
        self.assertTrue('/submit/app/details/' in ret.url, ret.url)
        app_slug = ret.url.split('/')[-1]

        # upload icon
        params = [['upload_image', Upload(ICON)]]
        add_csrf_token(ret, params)
        ret = self.post('/developers/app/%s/upload_icon' % app_slug,
            params=params)
        data = json.loads(ret.body)
        self.assertEqual(len(data['errors']), 0, data)
        icon_hash = data['upload_hash']

        # upload screenshot
        ret = self.get('/developers/submit/app/details/%s' % app_slug)
        params = [['upload_image', Upload(SCREENSHOT)]]
        add_csrf_token(ret, params)
        ret = self.post('/developers/app/%s/upload_image' % app_slug,
            params=params)
        data = json.loads(ret.body)
        self.assertEqual(len(data['errors']), 0, data)
        screenshot_hash = data['upload_hash']

        # fill in some more app details
        ret = self.get('/developers/submit/app/details/%s' % app_slug)
        params = [
            ['slug', app_slug],
            ['slug_en-us', app_slug],
            ['name_en-us', app_slug],
            ['summary_en-us', 'HA Test web app'],
            ['privacy_policy_en-us', 'We sell all your data!'],
            ['support_email_en-us', '*****@*****.**'],
            ['icon_upload_hash', icon_hash],
            ['icon_upload_hash_en-us', icon_hash],
            ['icon_type', 'image/png'],
            ['icon_type_en-us', 'image/png'],
            ['categories', '167'],
            ['categories_en-us', '167'],
            ['files-0-position', '0'],
            ['files-0-position_en-us', '0'],
            ['files-0-upload_hash', screenshot_hash],
            ['files-0-upload_hash_en-us', screenshot_hash],
            ['files-0-unsaved_image_type', 'image/png'],
            ['files-0-unsaved_image_type_en-us', 'image/png'],
            ['files-TOTAL_FORMS', '1'],
            ['files-TOTAL_FORMS_en-us', '1'],
            ['files-INITIAL_FORMS', '0'],
            ['files-INITIAL_FORMS_en-us', '0'],
            ['files-MAX_NUM_FORMS', '1'],
            ['files-MAX_NUM_FORMS_en-us', '1'],
        ]
        token = add_csrf_token(ret, params)
        if token:
            # work around creative code in app edit form
            params.append(['csrfmiddlewaretoken_en-us', token])

        # hack in the current_locale cookie
        cookies = self._browser.cookies
        morsel = Morsel()
        morsel.set('current_locale', 'en-us', 'en-us')
        for domain, value in cookies.items():
            value['/'].setdefault('current_locale', morsel)

        # fill details and submit
        ret = self.post(ret.url, params=params)

        # finally delete the app
        ret = self.get('/developers/app/%s/status' % app_slug)
        params = []
        add_csrf_token(ret, params)
        self.post('/developers/app/%s/delete' % app_slug, params=params)
    def submit_app(self):
        # try to submit an app
        ret = self.get('/developers/submit/app', ok_codes=[200, 302])

        # generate a random web-app manifest
        random_id = uuid.uuid1().hex
        manifest_url = WEBAPP % random_id

        # we need to accept the TOS once per user
        if 'read_dev_agreement' in ret.body:
            params = [['read_dev_agreement', 'True']]
            add_csrf_token(ret, params)
            ret = self.post(ret.url, params=params)

        # submit the manifest
        params = [['manifest', manifest_url]]
        add_csrf_token(ret, params)
        ret = self.post('/developers/upload-manifest', params=params)
        data = json.loads(ret.body)
        validation = data['validation']
        app_exists = False

        if isinstance(validation, dict) and 'messages' in validation:
            messages = [m['message'] for m in data['validation']['messages']]
            app_exists = 'already' in ' '.join(messages)

        # we should be able to test editing the app
        if app_exists:
            return

        if isinstance(validation, dict) and 'errors' in validation:
            self.assertEqual(data['validation']['errors'], 0, data)

        # now we can submit the app basics, first load the form again
        ret = self.get('/developers/submit/app/manifest')
        params = [['upload', data['upload']],
                  ['free', 'free-os'],
                  ['free', 'free-desktop'],
                  ['free', 'free-phone'],
                  ['free', 'free-tablet']]
        add_csrf_token(ret, params)
        ret = self.post('/developers/submit/app/manifest', params=params)
        self.assertTrue('/submit/app/details/' in ret.url, ret.url)
        app_slug = ret.url.split('/')[-1]

        # upload icon
        params = [['upload_image', Upload(ICON)]]
        add_csrf_token(ret, params)
        ret = self.post('/developers/app/%s/upload_icon' % app_slug,
            params=params)
        data = json.loads(ret.body)
        self.assertEqual(len(data['errors']), 0, data)
        icon_hash = data['upload_hash']

        # upload screenshot
        ret = self.get('/developers/submit/app/details/%s' % app_slug)
        params = [['upload_image', Upload(SCREENSHOT)]]
        add_csrf_token(ret, params)
        ret = self.post('/developers/app/%s/upload_image' % app_slug,
            params=params)
        data = json.loads(ret.body)
        self.assertEqual(len(data['errors']), 0, data)
        screenshot_hash = data['upload_hash']

        # fill in some more app details
        ret = self.get('/developers/submit/app/details/%s' % app_slug)
        params = [
            ['slug', app_slug],
            ['slug_en-us', app_slug],
            ['name_en-us', app_slug],
            ['summary_en-us', 'HA Test web app'],
            ['privacy_policy_en-us', 'We sell all your data!'],
            ['support_email_en-us', '*****@*****.**'],
            ['icon_upload_hash', icon_hash],
            ['icon_upload_hash_en-us', icon_hash],
            ['icon_type', 'image/png'],
            ['icon_type_en-us', 'image/png'],
            ['categories', '167'],
            ['categories_en-us', '167'],
            ['files-0-position', '0'],
            ['files-0-position_en-us', '0'],
            ['files-0-upload_hash', screenshot_hash],
            ['files-0-upload_hash_en-us', screenshot_hash],
            ['files-0-unsaved_image_type', 'image/png'],
            ['files-0-unsaved_image_type_en-us', 'image/png'],
            ['files-TOTAL_FORMS', '1'],
            ['files-TOTAL_FORMS_en-us', '1'],
            ['files-INITIAL_FORMS', '0'],
            ['files-INITIAL_FORMS_en-us', '0'],
            ['files-MAX_NUM_FORMS', '1'],
            ['files-MAX_NUM_FORMS_en-us', '1'],
        ]
        token = add_csrf_token(ret, params)
        if token:
            # work around creative code in app edit form
            params.append(['csrfmiddlewaretoken_en-us', token])

        # hack in the current_locale cookie
        cookies = self._browser.cookies
        morsel = Morsel()
        morsel.set('current_locale', 'en-us', 'en-us')
        for domain, value in cookies.items():
            value['/'].setdefault('current_locale', morsel)

        # fill details and submit
        ret = self.post(ret.url, params=params)

        # finally delete the app
        ret = self.get('/developers/app/%s/status' % app_slug)
        params = []
        add_csrf_token(ret, params)
        self.post('/developers/app/%s/delete' % app_slug, params=params)