prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Changing organization type doesn't work
  In identity tab, when I change the organization type to set Organization instead of Business, my changes are not saved. 
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `liberapay/security/authentication.py`
  Content:
  ```
  1 """Defines website authentication helpers.
  2 """
  3 import binascii
  4 
  5 from six.moves.urllib.parse import urlencode
  6 
  7 from aspen import Response
  8 
  9 from liberapay.constants import SESSION, SESSION_TIMEOUT
  10 from liberapay.exceptions import AuthRequired
  11 from liberapay.models.participant import Participant
  12 
  13 
  14 class _ANON(object):
  15     ANON = True
  16     is_admin = False
  17     id = None
  18     __bool__ = __nonzero__ = lambda *a: False
  19     get_tip_to = lambda self, tippee: Participant._zero_tip_dict(tippee)
  20     __repr__ = lambda self: '<ANON>'
  21 
  22 
  23 ANON = _ANON()
  24 
  25 
  26 def _get_body(request):
  27     try:
  28         body = request.body
  29     except Response:
  30         return
  31     if not isinstance(body, dict):
  32         return
  33     return body
  34 
  35 
  36 def sign_in_with_form_data(body, state):
  37     p = None
  38     _, website = state['_'], state['website']
  39 
  40     if body.get('log-in.id'):
  41         id = body.pop('log-in.id')
  42         k = 'email' if '@' in id else 'username'
  43         p = Participant.authenticate(
  44             k, 'password',
  45             id, body.pop('log-in.password')
  46         )
  47         if p and p.status == 'closed':
  48             p.update_status('active')
  49 
  50     elif body.get('sign-in.username'):
  51         if body.pop('sign-in.terms') != 'agree':
  52             raise Response(400, 'you have to agree to the terms')
  53         kind = body.pop('sign-in.kind')
  54         if kind not in ('individual', 'organization'):
  55             raise Response(400, 'bad kind')
  56         with website.db.get_cursor() as c:
  57             p = Participant.make_active(
  58                 body.pop('sign-in.username'), kind, body.pop('sign-in.password'),
  59                 cursor=c
  60             )
  61             p.add_email(body.pop('sign-in.email'), cursor=c)
  62         p.authenticated = True
  63 
  64     elif body.get('email-login.email'):
  65         email = body.pop('email-login.email')
  66         p = Participant._from_thing('email', email)
  67         if p:
  68             p.start_session()
  69             qs = {'log-in.id': p.id, 'log-in.token': p.session_token}
  70             p.send_email(
  71                 'password_reset',
  72                 email=email,
  73                 link=p.url('settings/', qs),
  74                 link_validity=SESSION_TIMEOUT,
  75             )
  76             state['email-login.sent-to'] = email
  77         else:
  78             state['sign-in.error'] = _(
  79                 "We didn't find any account whose primary email address is {0}.",
  80                 email
  81             )
  82         p = None
  83 
  84     return p
  85 
  86 
  87 def start_user_as_anon():
  88     """Make sure we always have a user object, regardless of exceptions during authentication.
  89     """
  90     return {'user': ANON}
  91 
  92 
  93 def authenticate_user_if_possible(request, state, user, _):
  94     """This signs the user in.
  95     """
  96     if request.line.uri.startswith('/assets/'):
  97         return
  98 
  99     # HTTP auth
  100     if 'Authorization' in request.headers:
  101         header = request.headers['authorization']
  102         if not header.startswith('Basic '):
  103             raise Response(401, 'Unsupported authentication method')
  104         try:
  105             creds = binascii.a2b_base64(header[len('Basic '):]).split(':', 1)
  106         except binascii.Error:
  107             raise Response(400, 'Malformed "Authorization" header')
  108         participant = Participant.authenticate('id', 'password', *creds)
  109         if not participant:
  110             raise Response(401)
  111         return {'user': participant}
  112 
  113     # Cookie and form auth
  114     # We want to try cookie auth first, but we want form auth to supersede it
  115     p = None
  116     response = state.setdefault('response', Response())
  117     if SESSION in request.headers.cookie:
  118         creds = request.headers.cookie[SESSION].value.split(':', 1)
  119         p = Participant.authenticate('id', 'session', *creds)
  120         if p:
  121             state['user'] = p
  122     session_p, p = p, None
  123     session_suffix = ''
  124     redirect_url = request.line.uri
  125     if request.method == 'POST':
  126         body = _get_body(request)
  127         if body:
  128             p = sign_in_with_form_data(body, state)
  129             carry_on = body.pop('email-login.carry-on', None)
  130             if not p and carry_on:
  131                 p_email = session_p and (
  132                     session_p.email or session_p.get_emails()[0].address
  133                 )
  134                 if p_email != carry_on:
  135                     state['email-login.carry-on'] = carry_on
  136                     raise AuthRequired
  137     elif request.method == 'GET' and request.qs.get('log-in.id'):
  138         id, token = request.qs.pop('log-in.id'), request.qs.pop('log-in.token')
  139         p = Participant.authenticate('id', 'session', id, token)
  140         if not p and (not session_p or session_p.id != id):
  141             raise Response(400, _("This login link is expired or invalid."))
  142         else:
  143             qs = '?' + urlencode(request.qs, doseq=True) if request.qs else ''
  144             redirect_url = request.path.raw + qs
  145             session_p = p
  146             session_suffix = '.em'
  147     if p:
  148         if session_p:
  149             session_p.sign_out(response.headers.cookie)
  150         p.sign_in(response.headers.cookie, session_suffix)
  151         state['user'] = p
  152         if request.body.pop('form.repost', None) != 'true':
  153             response.redirect(redirect_url)
  154 
  155 
  156 def add_auth_to_response(response, request=None, user=ANON):
  157     if request is None:
  158         return  # early parsing must've failed
  159     if request.line.uri.startswith('/assets/'):
  160         return  # assets never get auth headers
  161 
  162     if SESSION in request.headers.cookie:
  163         if not user.ANON:
  164             user.keep_signed_in(response.headers.cookie)
  165
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/liberapay/security/authentication.py b/liberapay/security/authentication.py
  --- a/liberapay/security/authentication.py
  +++ b/liberapay/security/authentication.py
  @@ -44,6 +44,8 @@
               k, 'password',
               id, body.pop('log-in.password')
           )
  +        if not p:
  +            state['sign-in.error'] = _("Bad username or password.")
           if p and p.status == 'closed':
               p.update_status('active')
  
verification_info: {"golden_diff": "diff --git a/liberapay/security/authentication.py b/liberapay/security/authentication.py\n--- a/liberapay/security/authentication.py\n+++ b/liberapay/security/authentication.py\n@@ -44,6 +44,8 @@\n             k, 'password',\n             id, body.pop('log-in.password')\n         )\n+        if not p:\n+            state['sign-in.error'] = _(\"Bad username or password.\")\n         if p and p.status == 'closed':\n             p.update_status('active')\n", "issue": "Changing organization type doesn't work\nIn identity tab, when I change the organization type to set Organization instead of Business, my changes are not saved. \n\n", "before_files": [{"content": "\"\"\"Defines website authentication helpers.\n\"\"\"\nimport binascii\n\nfrom six.moves.urllib.parse import urlencode\n\nfrom aspen import Response\n\nfrom liberapay.constants import SESSION, SESSION_TIMEOUT\nfrom liberapay.exceptions import AuthRequired\nfrom liberapay.models.participant import Participant\n\n\nclass _ANON(object):\n    ANON = True\n    is_admin = False\n    id = None\n    __bool__ = __nonzero__ = lambda *a: False\n    get_tip_to = lambda self, tippee: Participant._zero_tip_dict(tippee)\n    __repr__ = lambda self: '<ANON>'\n\n\nANON = _ANON()\n\n\ndef _get_body(request):\n    try:\n        body = request.body\n    except Response:\n        return\n    if not isinstance(body, dict):\n        return\n    return body\n\n\ndef sign_in_with_form_data(body, state):\n    p = None\n    _, website = state['_'], state['website']\n\n    if body.get('log-in.id'):\n        id = body.pop('log-in.id')\n        k = 'email' if '@' in id else 'username'\n        p = Participant.authenticate(\n            k, 'password',\n            id, body.pop('log-in.password')\n        )\n        if p and p.status == 'closed':\n            p.update_status('active')\n\n    elif body.get('sign-in.username'):\n        if body.pop('sign-in.terms') != 'agree':\n            raise Response(400, 'you have to agree to the terms')\n        kind = body.pop('sign-in.kind')\n        if kind not in ('individual', 'organization'):\n            raise Response(400, 'bad kind')\n        with website.db.get_cursor() as c:\n            p = Participant.make_active(\n                body.pop('sign-in.username'), kind, body.pop('sign-in.password'),\n                cursor=c\n            )\n            p.add_email(body.pop('sign-in.email'), cursor=c)\n        p.authenticated = True\n\n    elif body.get('email-login.email'):\n        email = body.pop('email-login.email')\n        p = Participant._from_thing('email', email)\n        if p:\n            p.start_session()\n            qs = {'log-in.id': p.id, 'log-in.token': p.session_token}\n            p.send_email(\n                'password_reset',\n                email=email,\n                link=p.url('settings/', qs),\n                link_validity=SESSION_TIMEOUT,\n            )\n            state['email-login.sent-to'] = email\n        else:\n            state['sign-in.error'] = _(\n                \"We didn't find any account whose primary email address is {0}.\",\n                email\n            )\n        p = None\n\n    return p\n\n\ndef start_user_as_anon():\n    \"\"\"Make sure we always have a user object, regardless of exceptions during authentication.\n    \"\"\"\n    return {'user': ANON}\n\n\ndef authenticate_user_if_possible(request, state, user, _):\n    \"\"\"This signs the user in.\n    \"\"\"\n    if request.line.uri.startswith('/assets/'):\n        return\n\n    # HTTP auth\n    if 'Authorization' in request.headers:\n        header = request.headers['authorization']\n        if not header.startswith('Basic '):\n            raise Response(401, 'Unsupported authentication method')\n        try:\n            creds = binascii.a2b_base64(header[len('Basic '):]).split(':', 1)\n        except binascii.Error:\n            raise Response(400, 'Malformed \"Authorization\" header')\n        participant = Participant.authenticate('id', 'password', *creds)\n        if not participant:\n            raise Response(401)\n        return {'user': participant}\n\n    # Cookie and form auth\n    # We want to try cookie auth first, but we want form auth to supersede it\n    p = None\n    response = state.setdefault('response', Response())\n    if SESSION in request.headers.cookie:\n        creds = request.headers.cookie[SESSION].value.split(':', 1)\n        p = Participant.authenticate('id', 'session', *creds)\n        if p:\n            state['user'] = p\n    session_p, p = p, None\n    session_suffix = ''\n    redirect_url = request.line.uri\n    if request.method == 'POST':\n        body = _get_body(request)\n        if body:\n            p = sign_in_with_form_data(body, state)\n            carry_on = body.pop('email-login.carry-on', None)\n            if not p and carry_on:\n                p_email = session_p and (\n                    session_p.email or session_p.get_emails()[0].address\n                )\n                if p_email != carry_on:\n                    state['email-login.carry-on'] = carry_on\n                    raise AuthRequired\n    elif request.method == 'GET' and request.qs.get('log-in.id'):\n        id, token = request.qs.pop('log-in.id'), request.qs.pop('log-in.token')\n        p = Participant.authenticate('id', 'session', id, token)\n        if not p and (not session_p or session_p.id != id):\n            raise Response(400, _(\"This login link is expired or invalid.\"))\n        else:\n            qs = '?' + urlencode(request.qs, doseq=True) if request.qs else ''\n            redirect_url = request.path.raw + qs\n            session_p = p\n            session_suffix = '.em'\n    if p:\n        if session_p:\n            session_p.sign_out(response.headers.cookie)\n        p.sign_in(response.headers.cookie, session_suffix)\n        state['user'] = p\n        if request.body.pop('form.repost', None) != 'true':\n            response.redirect(redirect_url)\n\n\ndef add_auth_to_response(response, request=None, user=ANON):\n    if request is None:\n        return  # early parsing must've failed\n    if request.line.uri.startswith('/assets/'):\n        return  # assets never get auth headers\n\n    if SESSION in request.headers.cookie:\n        if not user.ANON:\n            user.keep_signed_in(response.headers.cookie)\n", "path": "liberapay/security/authentication.py"}], "after_files": [{"content": "\"\"\"Defines website authentication helpers.\n\"\"\"\nimport binascii\n\nfrom six.moves.urllib.parse import urlencode\n\nfrom aspen import Response\n\nfrom liberapay.constants import SESSION, SESSION_TIMEOUT\nfrom liberapay.exceptions import AuthRequired\nfrom liberapay.models.participant import Participant\n\n\nclass _ANON(object):\n    ANON = True\n    is_admin = False\n    id = None\n    __bool__ = __nonzero__ = lambda *a: False\n    get_tip_to = lambda self, tippee: Participant._zero_tip_dict(tippee)\n    __repr__ = lambda self: '<ANON>'\n\n\nANON = _ANON()\n\n\ndef _get_body(request):\n    try:\n        body = request.body\n    except Response:\n        return\n    if not isinstance(body, dict):\n        return\n    return body\n\n\ndef sign_in_with_form_data(body, state):\n    p = None\n    _, website = state['_'], state['website']\n\n    if body.get('log-in.id'):\n        id = body.pop('log-in.id')\n        k = 'email' if '@' in id else 'username'\n        p = Participant.authenticate(\n            k, 'password',\n            id, body.pop('log-in.password')\n        )\n        if not p:\n            state['sign-in.error'] = _(\"Bad username or password.\")\n        if p and p.status == 'closed':\n            p.update_status('active')\n\n    elif body.get('sign-in.username'):\n        if body.pop('sign-in.terms') != 'agree':\n            raise Response(400, 'you have to agree to the terms')\n        kind = body.pop('sign-in.kind')\n        if kind not in ('individual', 'organization'):\n            raise Response(400, 'bad kind')\n        with website.db.get_cursor() as c:\n            p = Participant.make_active(\n                body.pop('sign-in.username'), kind, body.pop('sign-in.password'),\n                cursor=c\n            )\n            p.add_email(body.pop('sign-in.email'), cursor=c)\n        p.authenticated = True\n\n    elif body.get('email-login.email'):\n        email = body.pop('email-login.email')\n        p = Participant._from_thing('email', email)\n        if p:\n            p.start_session()\n            qs = {'log-in.id': p.id, 'log-in.token': p.session_token}\n            p.send_email(\n                'password_reset',\n                email=email,\n                link=p.url('settings/', qs),\n                link_validity=SESSION_TIMEOUT,\n            )\n            state['email-login.sent-to'] = email\n        else:\n            state['sign-in.error'] = _(\n                \"We didn't find any account whose primary email address is {0}.\",\n                email\n            )\n        p = None\n\n    return p\n\n\ndef start_user_as_anon():\n    \"\"\"Make sure we always have a user object, regardless of exceptions during authentication.\n    \"\"\"\n    return {'user': ANON}\n\n\ndef authenticate_user_if_possible(request, state, user, _):\n    \"\"\"This signs the user in.\n    \"\"\"\n    if request.line.uri.startswith('/assets/'):\n        return\n\n    # HTTP auth\n    if 'Authorization' in request.headers:\n        header = request.headers['authorization']\n        if not header.startswith('Basic '):\n            raise Response(401, 'Unsupported authentication method')\n        try:\n            creds = binascii.a2b_base64(header[len('Basic '):]).split(':', 1)\n        except binascii.Error:\n            raise Response(400, 'Malformed \"Authorization\" header')\n        participant = Participant.authenticate('id', 'password', *creds)\n        if not participant:\n            raise Response(401)\n        return {'user': participant}\n\n    # Cookie and form auth\n    # We want to try cookie auth first, but we want form auth to supersede it\n    p = None\n    response = state.setdefault('response', Response())\n    if SESSION in request.headers.cookie:\n        creds = request.headers.cookie[SESSION].value.split(':', 1)\n        p = Participant.authenticate('id', 'session', *creds)\n        if p:\n            state['user'] = p\n    session_p, p = p, None\n    session_suffix = ''\n    redirect_url = request.line.uri\n    if request.method == 'POST':\n        body = _get_body(request)\n        if body:\n            p = sign_in_with_form_data(body, state)\n            carry_on = body.pop('email-login.carry-on', None)\n            if not p and carry_on:\n                p_email = session_p and (\n                    session_p.email or session_p.get_emails()[0].address\n                )\n                if p_email != carry_on:\n                    state['email-login.carry-on'] = carry_on\n                    raise AuthRequired\n    elif request.method == 'GET' and request.qs.get('log-in.id'):\n        id, token = request.qs.pop('log-in.id'), request.qs.pop('log-in.token')\n        p = Participant.authenticate('id', 'session', id, token)\n        if not p and (not session_p or session_p.id != id):\n            raise Response(400, _(\"This login link is expired or invalid.\"))\n        else:\n            qs = '?' + urlencode(request.qs, doseq=True) if request.qs else ''\n            redirect_url = request.path.raw + qs\n            session_p = p\n            session_suffix = '.em'\n    if p:\n        if session_p:\n            session_p.sign_out(response.headers.cookie)\n        p.sign_in(response.headers.cookie, session_suffix)\n        state['user'] = p\n        if request.body.pop('form.repost', None) != 'true':\n            response.redirect(redirect_url)\n\n\ndef add_auth_to_response(response, request=None, user=ANON):\n    if request is None:\n        return  # early parsing must've failed\n    if request.line.uri.startswith('/assets/'):\n        return  # assets never get auth headers\n\n    if SESSION in request.headers.cookie:\n        if not user.ANON:\n            user.keep_signed_in(response.headers.cookie)\n", "path": "liberapay/security/authentication.py"}]}