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()

```

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')

{"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"}]}