We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Ensure UP app only lists projects that user has access


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `akvo/rsr/views/account.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 
3 """Akvo RSR is covered by the GNU Affero General Public License.
4 
5 See more details in the license.txt file located at the root folder of the
6 Akvo RSR module. For additional details on the GNU license please
7 see < http://www.gnu.org/licenses/agpl.html >.
8 """
9 
10 import re
11 import json
12 
13 from lxml import etree
14 from tastypie.models import ApiKey
15 
16 from akvo.rsr.forms import RegisterForm, InvitedUserForm, PasswordResetForm
17 from akvo.rsr.models import Employment
18 from akvo.utils import rsr_send_mail
19 
20 from django.conf import settings
21 from django.contrib.auth import login, logout, authenticate, get_user_model
22 from django.contrib.auth.forms import AuthenticationForm
23 from django.core.exceptions import ObjectDoesNotExist, ValidationError
24 from django.core.signing import TimestampSigner, BadSignature
25 from django.http import (HttpResponse, HttpResponseRedirect,
26                          HttpResponseForbidden)
27 from django.shortcuts import redirect, render
28 
29 from registration.models import RegistrationProfile
30 
31 from django.views.decorators.csrf import csrf_exempt
32 from django.views.decorators.http import require_POST
33 
34 
35 def register(request):
36     """Register form."""
37     if request.method == 'POST':
38         form = RegisterForm(data=request.POST, files=request.FILES)
39         if form.is_valid():
40             #  Honeypot field filled in? If so don't register and redirect to home page
41             if request.POST.get('hp_title'):
42                 return redirect('index')
43             user = form.save(request)
44             return render(
45                 request,
46                 'registration/register_complete.html',
47                 {'new_user': user},
48             )
49     else:
50         form = RegisterForm()
51     return render(
52         request,
53         'registration/register.html',
54         {'form': form, 'password_length': settings.PASSWORD_MINIMUM_LENGTH}
55     )
56 
57 
58 def activate(request, activation_key, extra_context=None):
59     """Activate resouce.
60 
61     Activate a User's account, if their key is valid and hasn't expired.
62     Any values passed in the keyword argument "extra_context"
63     (which must be a dictionary) will be added to the context.
64     Any values in "extra_context" which are callable will be called prior to
65     being added to the context.
66     """
67     sha = re.compile('^[a-f0-9]{40}$')
68     activation_key = activation_key.lower()
69 
70     if sha.search(activation_key):
71         try:
72             registration_profile = RegistrationProfile.objects.get(
73                 activation_key=activation_key)
74         except RegistrationProfile.DoesNotExist:
75             user = False
76         else:
77             if not registration_profile.activation_key_expired():
78                 registration_profile.activation_key = RegistrationProfile.ACTIVATED
79                 registration_profile.save()
80                 user = registration_profile.user
81                 user.is_active = True
82                 user.save()
83 
84                 # Log in user without password, using custom backend
85                 user = authenticate(username=user.username, no_password=True)
86                 login(request, user)
87     if extra_context is None:
88         extra_context = {}
89     context = dict()
90     for key, value in extra_context.items():
91         context[key] = callable(value) and value() or value
92     return render(
93         request,
94         'registration/activate.html',
95         context
96     )
97 
98 
99 def invite_activate(request, inviting_pk, user_pk, employment_pk, token_date, token):
100     """
101     Activate a user that has been invited to use RSR.
102 
103     :param request: the request
104     :param inviting_pk: the invitee user's primary key
105     :param user_pk: the invited user's primary key
106     :param employment_pk: the employment's primary key
107     :param token_date: the first part of the token
108     :param token: the second part of the token
109     """
110 
111     def approve_employment(invitee, invited, empl):
112         """
113         Approves the employment and sends a mail to the user that has invited the new user.
114 
115         :param invitee: the invitee user's instance
116         :param invited: the invited user's instance
117         :param empl: the employment's instance
118         """
119         empl.approve(invitee)
120 
121         if invitee:
122             # Send notification email to inviting user
123             rsr_send_mail(
124                 [invitee.email],
125                 subject='registration/inviting_user_notification_subject.txt',
126                 message='registration/inviting_user_notification_message.txt',
127                 html_message='registration/inviting_user_notification_message.html',
128                 subject_context={
129                     'user': invited,
130                 },
131                 msg_context={
132                     'invited_user': invited,
133                     'inviting_user': invitee,
134                     'organisation': empl.organisation,
135                 }
136             )
137 
138     def login_and_redirect(req, invited):
139         """
140         Log the invited user in and redirect to the My projects page in MyRSR.
141 
142         :param req: the request
143         :param invited: the invited user's instance
144         """
145         invited = authenticate(username=invited.username, no_password=True)
146         login(request, invited)
147         return redirect('my_projects')
148 
149     bad_link, user, inviting_user, employment = False, None, None, None
150 
151     try:
152         user = get_user_model().objects.get(pk=user_pk)
153         inviting_user = get_user_model().objects.get(pk=inviting_pk)
154         employment = Employment.objects.get(pk=employment_pk) if int(employment_pk) != 0 else None
155     except ObjectDoesNotExist:
156         bad_link = True
157 
158     try:
159         TimestampSigner().unsign(':'.join([user.email, token_date, token]))
160     except BadSignature:
161         bad_link = True
162 
163     if user and user.is_active:
164         if employment and employment.is_approved:
165             # User is active and employment is approved, so nothing to do here
166             return login_and_redirect(request, user)
167         elif employment and not bad_link:
168             # Employment is not yet approved, and link is ok.
169             # Approve employment and log user in.
170             approve_employment(inviting_user, user, employment)
171             return login_and_redirect(request, user)
172         elif employment is None and not bad_link and request.GET.get('project_invite') is not None:
173             return login_and_redirect(request, user)
174 
175     if request.method == 'POST':
176         form = InvitedUserForm(user=user, data=request.POST)
177         if form.is_valid():
178             # Approve employment and save new user details
179             form.save(request)
180             if employment is not None:
181                 approve_employment(inviting_user, user, employment)
182             return login_and_redirect(request, user)
183     else:
184         form = InvitedUserForm(user=user)
185 
186     context = {
187         'form': form,
188         'bad_link': bad_link,
189         'password_length': settings.PASSWORD_MINIMUM_LENGTH
190     }
191     return render(request, 'registration/invite_activate.html', context)
192 
193 
194 def sign_in(request):
195     """Sign in.
196 
197     POST have two variants with username & email:
198      - username > normal sign in
199      - email > password reset workflow
200     """
201     form = AuthenticationForm()
202     reset_form = PasswordResetForm()
203     if request.method == "POST" and 'username' in request.POST:
204         form = AuthenticationForm(data=request.POST)
205         if form.is_valid():
206             login(request, form.get_user())
207             next_page = request.GET.get('next')
208             return HttpResponseRedirect(next_page) if next_page else redirect('my_projects')
209     # Password reset on sign in page
210     elif request.method == "POST" and 'email' in request.POST:
211         reset_form = PasswordResetForm(data=request.POST)
212         if reset_form.is_valid():
213             reset_form.save(domain_override=settings.RSR_DOMAIN)
214         return HttpResponse()
215     return render(request, 'sign_in.html', {'form': form, 'reset_form': reset_form})
216 
217 
218 def sign_out(request):
219     """Log out resouce."""
220     logout(request)
221     return redirect('index')
222 
223 
224 def api_key_xml_response(user, orgs):
225     """Build the XML response.
226 
227     This is used by the Up app - so make sure they match on change.
228     """
229     xml_root = etree.Element("credentials")
230 
231     # User
232     user_id_element = etree.SubElement(xml_root, "user_id")
233     user_id_element.text = str(user.id)
234     user_username_element = etree.SubElement(xml_root, "username")
235     user_username_element.text = user.username
236 
237     # Organisations
238     for org in orgs:
239         org_id_element = etree.SubElement(xml_root, "org_id")
240         org_id_element.text = str(org.id)
241 
242     # API key
243     api_key_element = etree.SubElement(xml_root, "api_key")
244     api_key_element.text = ApiKey.objects.get_or_create(user=user)[0].key
245 
246     # Published and editable projects
247     projects = orgs.all_projects().published()
248     pub_projs_element = etree.SubElement(xml_root, "published_projects")
249     edit_projs_element = etree.SubElement(xml_root, "allow_edit_projects")
250     for project in projects:
251         project_id_element = etree.SubElement(pub_projs_element, "id")
252         project_id_element.text = str(project.id)
253         if user.has_perm('rsr.change_project', project):
254             project_id_element = etree.SubElement(edit_projs_element, "id")
255             project_id_element.text = str(project.id)
256 
257     return etree.tostring(etree.ElementTree(xml_root))
258 
259 
260 def api_key_json_response(user, orgs):
261     """
262     Build the JSON response. This is used by the Up app - so make sure they match on change.
263     """
264     response_data = dict()
265 
266     # User
267     response_data["user_id"] = user.id
268     response_data["username"] = user.username
269 
270     # Organisations
271     response_data["organisations"] = [org.id for org in orgs]
272 
273     # API key
274     response_data["api_key"] = ApiKey.objects.get_or_create(user=user)[0].key
275 
276     # Published projects
277     projects = orgs.all_projects().published()
278     response_data["published_projects"] = [p.id for p in projects]
279 
280     # Editable projects
281     perm = 'rsr.change_project'
282     perm_filter = user.get_permission_filter(perm, '')
283     response_data["allow_edit_projects"] = list(projects.filter(perm_filter).values_list('id', flat=True))
284 
285     return json.dumps(response_data)
286 
287 
288 @require_POST
289 @csrf_exempt
290 def api_key(request):
291     """On successful user credentials returns an auth token for API usage.
292 
293     Since RSR changed in v3 to allow users without an organisation we need to
294     introduce a way to make old Up apps work as before but new ones support
295     users without any connected organisations.
296     """
297     request_format = request.GET.get('format', 'xml')
298     username = request.POST.get('username', False)
299     password = request.POST.get('password', False)
300     handles_unemployed = bool(request.POST.get("handles_unemployed", False))
301 
302     if username and password:
303         try:
304             user = authenticate(username=username, password=password)
305         except ValidationError:
306             user = None
307         if user is not None and user.is_active:
308             orgs = user.approved_organisations()
309             if orgs or handles_unemployed:
310                 if request_format == 'xml':
311                     return HttpResponse(api_key_xml_response(user, orgs),
312                                         content_type="text/xml")
313                 elif request_format == 'json':
314                     return HttpResponse(api_key_json_response(user, orgs),
315                                         content_type="application/json")
316     return HttpResponseForbidden()
317
```
--- 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/akvo/rsr/views/account.py b/akvo/rsr/views/account.py
--- a/akvo/rsr/views/account.py
+++ b/akvo/rsr/views/account.py
@@ -244,7 +244,7 @@
     api_key_element.text = ApiKey.objects.get_or_create(user=user)[0].key
 
     # Published and editable projects
-    projects = orgs.all_projects().published()
+    projects = user.my_projects.published()
     pub_projs_element = etree.SubElement(xml_root, "published_projects")
     edit_projs_element = etree.SubElement(xml_root, "allow_edit_projects")
     for project in projects:
@@ -274,7 +274,7 @@
     response_data["api_key"] = ApiKey.objects.get_or_create(user=user)[0].key
 
     # Published projects
-    projects = orgs.all_projects().published()
+    projects = user.my_projects().published()
     response_data["published_projects"] = [p.id for p in projects]
 
     # Editable projects

{"golden_diff": "diff --git a/akvo/rsr/views/account.py b/akvo/rsr/views/account.py\n--- a/akvo/rsr/views/account.py\n+++ b/akvo/rsr/views/account.py\n@@ -244,7 +244,7 @@\n     api_key_element.text = ApiKey.objects.get_or_create(user=user)[0].key\n \n     # Published and editable projects\n-    projects = orgs.all_projects().published()\n+    projects = user.my_projects.published()\n     pub_projs_element = etree.SubElement(xml_root, \"published_projects\")\n     edit_projs_element = etree.SubElement(xml_root, \"allow_edit_projects\")\n     for project in projects:\n@@ -274,7 +274,7 @@\n     response_data[\"api_key\"] = ApiKey.objects.get_or_create(user=user)[0].key\n \n     # Published projects\n-    projects = orgs.all_projects().published()\n+    projects = user.my_projects().published()\n     response_data[\"published_projects\"] = [p.id for p in projects]\n \n     # Editable projects\n", "issue": "Ensure UP app only lists projects that user has access\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\n\"\"\"Akvo RSR is covered by the GNU Affero General Public License.\n\nSee more details in the license.txt file located at the root folder of the\nAkvo RSR module. For additional details on the GNU license please\nsee < http://www.gnu.org/licenses/agpl.html >.\n\"\"\"\n\nimport re\nimport json\n\nfrom lxml import etree\nfrom tastypie.models import ApiKey\n\nfrom akvo.rsr.forms import RegisterForm, InvitedUserForm, PasswordResetForm\nfrom akvo.rsr.models import Employment\nfrom akvo.utils import rsr_send_mail\n\nfrom django.conf import settings\nfrom django.contrib.auth import login, logout, authenticate, get_user_model\nfrom django.contrib.auth.forms import AuthenticationForm\nfrom django.core.exceptions import ObjectDoesNotExist, ValidationError\nfrom django.core.signing import TimestampSigner, BadSignature\nfrom django.http import (HttpResponse, HttpResponseRedirect,\n                         HttpResponseForbidden)\nfrom django.shortcuts import redirect, render\n\nfrom registration.models import RegistrationProfile\n\nfrom django.views.decorators.csrf import csrf_exempt\nfrom django.views.decorators.http import require_POST\n\n\ndef register(request):\n    \"\"\"Register form.\"\"\"\n    if request.method == 'POST':\n        form = RegisterForm(data=request.POST, files=request.FILES)\n        if form.is_valid():\n            #  Honeypot field filled in? If so don't register and redirect to home page\n            if request.POST.get('hp_title'):\n                return redirect('index')\n            user = form.save(request)\n            return render(\n                request,\n                'registration/register_complete.html',\n                {'new_user': user},\n            )\n    else:\n        form = RegisterForm()\n    return render(\n        request,\n        'registration/register.html',\n        {'form': form, 'password_length': settings.PASSWORD_MINIMUM_LENGTH}\n    )\n\n\ndef activate(request, activation_key, extra_context=None):\n    \"\"\"Activate resouce.\n\n    Activate a User's account, if their key is valid and hasn't expired.\n    Any values passed in the keyword argument \"extra_context\"\n    (which must be a dictionary) will be added to the context.\n    Any values in \"extra_context\" which are callable will be called prior to\n    being added to the context.\n    \"\"\"\n    sha = re.compile('^[a-f0-9]{40}$')\n    activation_key = activation_key.lower()\n\n    if sha.search(activation_key):\n        try:\n            registration_profile = RegistrationProfile.objects.get(\n                activation_key=activation_key)\n        except RegistrationProfile.DoesNotExist:\n            user = False\n        else:\n            if not registration_profile.activation_key_expired():\n                registration_profile.activation_key = RegistrationProfile.ACTIVATED\n                registration_profile.save()\n                user = registration_profile.user\n                user.is_active = True\n                user.save()\n\n                # Log in user without password, using custom backend\n                user = authenticate(username=user.username, no_password=True)\n                login(request, user)\n    if extra_context is None:\n        extra_context = {}\n    context = dict()\n    for key, value in extra_context.items():\n        context[key] = callable(value) and value() or value\n    return render(\n        request,\n        'registration/activate.html',\n        context\n    )\n\n\ndef invite_activate(request, inviting_pk, user_pk, employment_pk, token_date, token):\n    \"\"\"\n    Activate a user that has been invited to use RSR.\n\n    :param request: the request\n    :param inviting_pk: the invitee user's primary key\n    :param user_pk: the invited user's primary key\n    :param employment_pk: the employment's primary key\n    :param token_date: the first part of the token\n    :param token: the second part of the token\n    \"\"\"\n\n    def approve_employment(invitee, invited, empl):\n        \"\"\"\n        Approves the employment and sends a mail to the user that has invited the new user.\n\n        :param invitee: the invitee user's instance\n        :param invited: the invited user's instance\n        :param empl: the employment's instance\n        \"\"\"\n        empl.approve(invitee)\n\n        if invitee:\n            # Send notification email to inviting user\n            rsr_send_mail(\n                [invitee.email],\n                subject='registration/inviting_user_notification_subject.txt',\n                message='registration/inviting_user_notification_message.txt',\n                html_message='registration/inviting_user_notification_message.html',\n                subject_context={\n                    'user': invited,\n                },\n                msg_context={\n                    'invited_user': invited,\n                    'inviting_user': invitee,\n                    'organisation': empl.organisation,\n                }\n            )\n\n    def login_and_redirect(req, invited):\n        \"\"\"\n        Log the invited user in and redirect to the My projects page in MyRSR.\n\n        :param req: the request\n        :param invited: the invited user's instance\n        \"\"\"\n        invited = authenticate(username=invited.username, no_password=True)\n        login(request, invited)\n        return redirect('my_projects')\n\n    bad_link, user, inviting_user, employment = False, None, None, None\n\n    try:\n        user = get_user_model().objects.get(pk=user_pk)\n        inviting_user = get_user_model().objects.get(pk=inviting_pk)\n        employment = Employment.objects.get(pk=employment_pk) if int(employment_pk) != 0 else None\n    except ObjectDoesNotExist:\n        bad_link = True\n\n    try:\n        TimestampSigner().unsign(':'.join([user.email, token_date, token]))\n    except BadSignature:\n        bad_link = True\n\n    if user and user.is_active:\n        if employment and employment.is_approved:\n            # User is active and employment is approved, so nothing to do here\n            return login_and_redirect(request, user)\n        elif employment and not bad_link:\n            # Employment is not yet approved, and link is ok.\n            # Approve employment and log user in.\n            approve_employment(inviting_user, user, employment)\n            return login_and_redirect(request, user)\n        elif employment is None and not bad_link and request.GET.get('project_invite') is not None:\n            return login_and_redirect(request, user)\n\n    if request.method == 'POST':\n        form = InvitedUserForm(user=user, data=request.POST)\n        if form.is_valid():\n            # Approve employment and save new user details\n            form.save(request)\n            if employment is not None:\n                approve_employment(inviting_user, user, employment)\n            return login_and_redirect(request, user)\n    else:\n        form = InvitedUserForm(user=user)\n\n    context = {\n        'form': form,\n        'bad_link': bad_link,\n        'password_length': settings.PASSWORD_MINIMUM_LENGTH\n    }\n    return render(request, 'registration/invite_activate.html', context)\n\n\ndef sign_in(request):\n    \"\"\"Sign in.\n\n    POST have two variants with username & email:\n     - username > normal sign in\n     - email > password reset workflow\n    \"\"\"\n    form = AuthenticationForm()\n    reset_form = PasswordResetForm()\n    if request.method == \"POST\" and 'username' in request.POST:\n        form = AuthenticationForm(data=request.POST)\n        if form.is_valid():\n            login(request, form.get_user())\n            next_page = request.GET.get('next')\n            return HttpResponseRedirect(next_page) if next_page else redirect('my_projects')\n    # Password reset on sign in page\n    elif request.method == \"POST\" and 'email' in request.POST:\n        reset_form = PasswordResetForm(data=request.POST)\n        if reset_form.is_valid():\n            reset_form.save(domain_override=settings.RSR_DOMAIN)\n        return HttpResponse()\n    return render(request, 'sign_in.html', {'form': form, 'reset_form': reset_form})\n\n\ndef sign_out(request):\n    \"\"\"Log out resouce.\"\"\"\n    logout(request)\n    return redirect('index')\n\n\ndef api_key_xml_response(user, orgs):\n    \"\"\"Build the XML response.\n\n    This is used by the Up app - so make sure they match on change.\n    \"\"\"\n    xml_root = etree.Element(\"credentials\")\n\n    # User\n    user_id_element = etree.SubElement(xml_root, \"user_id\")\n    user_id_element.text = str(user.id)\n    user_username_element = etree.SubElement(xml_root, \"username\")\n    user_username_element.text = user.username\n\n    # Organisations\n    for org in orgs:\n        org_id_element = etree.SubElement(xml_root, \"org_id\")\n        org_id_element.text = str(org.id)\n\n    # API key\n    api_key_element = etree.SubElement(xml_root, \"api_key\")\n    api_key_element.text = ApiKey.objects.get_or_create(user=user)[0].key\n\n    # Published and editable projects\n    projects = orgs.all_projects().published()\n    pub_projs_element = etree.SubElement(xml_root, \"published_projects\")\n    edit_projs_element = etree.SubElement(xml_root, \"allow_edit_projects\")\n    for project in projects:\n        project_id_element = etree.SubElement(pub_projs_element, \"id\")\n        project_id_element.text = str(project.id)\n        if user.has_perm('rsr.change_project', project):\n            project_id_element = etree.SubElement(edit_projs_element, \"id\")\n            project_id_element.text = str(project.id)\n\n    return etree.tostring(etree.ElementTree(xml_root))\n\n\ndef api_key_json_response(user, orgs):\n    \"\"\"\n    Build the JSON response. This is used by the Up app - so make sure they match on change.\n    \"\"\"\n    response_data = dict()\n\n    # User\n    response_data[\"user_id\"] = user.id\n    response_data[\"username\"] = user.username\n\n    # Organisations\n    response_data[\"organisations\"] = [org.id for org in orgs]\n\n    # API key\n    response_data[\"api_key\"] = ApiKey.objects.get_or_create(user=user)[0].key\n\n    # Published projects\n    projects = orgs.all_projects().published()\n    response_data[\"published_projects\"] = [p.id for p in projects]\n\n    # Editable projects\n    perm = 'rsr.change_project'\n    perm_filter = user.get_permission_filter(perm, '')\n    response_data[\"allow_edit_projects\"] = list(projects.filter(perm_filter).values_list('id', flat=True))\n\n    return json.dumps(response_data)\n\n\n@require_POST\n@csrf_exempt\ndef api_key(request):\n    \"\"\"On successful user credentials returns an auth token for API usage.\n\n    Since RSR changed in v3 to allow users without an organisation we need to\n    introduce a way to make old Up apps work as before but new ones support\n    users without any connected organisations.\n    \"\"\"\n    request_format = request.GET.get('format', 'xml')\n    username = request.POST.get('username', False)\n    password = request.POST.get('password', False)\n    handles_unemployed = bool(request.POST.get(\"handles_unemployed\", False))\n\n    if username and password:\n        try:\n            user = authenticate(username=username, password=password)\n        except ValidationError:\n            user = None\n        if user is not None and user.is_active:\n            orgs = user.approved_organisations()\n            if orgs or handles_unemployed:\n                if request_format == 'xml':\n                    return HttpResponse(api_key_xml_response(user, orgs),\n                                        content_type=\"text/xml\")\n                elif request_format == 'json':\n                    return HttpResponse(api_key_json_response(user, orgs),\n                                        content_type=\"application/json\")\n    return HttpResponseForbidden()\n", "path": "akvo/rsr/views/account.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\n\"\"\"Akvo RSR is covered by the GNU Affero General Public License.\n\nSee more details in the license.txt file located at the root folder of the\nAkvo RSR module. For additional details on the GNU license please\nsee < http://www.gnu.org/licenses/agpl.html >.\n\"\"\"\n\nimport re\nimport json\n\nfrom lxml import etree\nfrom tastypie.models import ApiKey\n\nfrom akvo.rsr.forms import RegisterForm, InvitedUserForm, PasswordResetForm\nfrom akvo.rsr.models import Employment\nfrom akvo.utils import rsr_send_mail\n\nfrom django.conf import settings\nfrom django.contrib.auth import login, logout, authenticate, get_user_model\nfrom django.contrib.auth.forms import AuthenticationForm\nfrom django.core.exceptions import ObjectDoesNotExist, ValidationError\nfrom django.core.signing import TimestampSigner, BadSignature\nfrom django.http import (HttpResponse, HttpResponseRedirect,\n                         HttpResponseForbidden)\nfrom django.shortcuts import redirect, render\n\nfrom registration.models import RegistrationProfile\n\nfrom django.views.decorators.csrf import csrf_exempt\nfrom django.views.decorators.http import require_POST\n\n\ndef register(request):\n    \"\"\"Register form.\"\"\"\n    if request.method == 'POST':\n        form = RegisterForm(data=request.POST, files=request.FILES)\n        if form.is_valid():\n            #  Honeypot field filled in? If so don't register and redirect to home page\n            if request.POST.get('hp_title'):\n                return redirect('index')\n            user = form.save(request)\n            return render(\n                request,\n                'registration/register_complete.html',\n                {'new_user': user},\n            )\n    else:\n        form = RegisterForm()\n    return render(\n        request,\n        'registration/register.html',\n        {'form': form, 'password_length': settings.PASSWORD_MINIMUM_LENGTH}\n    )\n\n\ndef activate(request, activation_key, extra_context=None):\n    \"\"\"Activate resouce.\n\n    Activate a User's account, if their key is valid and hasn't expired.\n    Any values passed in the keyword argument \"extra_context\"\n    (which must be a dictionary) will be added to the context.\n    Any values in \"extra_context\" which are callable will be called prior to\n    being added to the context.\n    \"\"\"\n    sha = re.compile('^[a-f0-9]{40}$')\n    activation_key = activation_key.lower()\n\n    if sha.search(activation_key):\n        try:\n            registration_profile = RegistrationProfile.objects.get(\n                activation_key=activation_key)\n        except RegistrationProfile.DoesNotExist:\n            user = False\n        else:\n            if not registration_profile.activation_key_expired():\n                registration_profile.activation_key = RegistrationProfile.ACTIVATED\n                registration_profile.save()\n                user = registration_profile.user\n                user.is_active = True\n                user.save()\n\n                # Log in user without password, using custom backend\n                user = authenticate(username=user.username, no_password=True)\n                login(request, user)\n    if extra_context is None:\n        extra_context = {}\n    context = dict()\n    for key, value in extra_context.items():\n        context[key] = callable(value) and value() or value\n    return render(\n        request,\n        'registration/activate.html',\n        context\n    )\n\n\ndef invite_activate(request, inviting_pk, user_pk, employment_pk, token_date, token):\n    \"\"\"\n    Activate a user that has been invited to use RSR.\n\n    :param request: the request\n    :param inviting_pk: the invitee user's primary key\n    :param user_pk: the invited user's primary key\n    :param employment_pk: the employment's primary key\n    :param token_date: the first part of the token\n    :param token: the second part of the token\n    \"\"\"\n\n    def approve_employment(invitee, invited, empl):\n        \"\"\"\n        Approves the employment and sends a mail to the user that has invited the new user.\n\n        :param invitee: the invitee user's instance\n        :param invited: the invited user's instance\n        :param empl: the employment's instance\n        \"\"\"\n        empl.approve(invitee)\n\n        if invitee:\n            # Send notification email to inviting user\n            rsr_send_mail(\n                [invitee.email],\n                subject='registration/inviting_user_notification_subject.txt',\n                message='registration/inviting_user_notification_message.txt',\n                html_message='registration/inviting_user_notification_message.html',\n                subject_context={\n                    'user': invited,\n                },\n                msg_context={\n                    'invited_user': invited,\n                    'inviting_user': invitee,\n                    'organisation': empl.organisation,\n                }\n            )\n\n    def login_and_redirect(req, invited):\n        \"\"\"\n        Log the invited user in and redirect to the My projects page in MyRSR.\n\n        :param req: the request\n        :param invited: the invited user's instance\n        \"\"\"\n        invited = authenticate(username=invited.username, no_password=True)\n        login(request, invited)\n        return redirect('my_projects')\n\n    bad_link, user, inviting_user, employment = False, None, None, None\n\n    try:\n        user = get_user_model().objects.get(pk=user_pk)\n        inviting_user = get_user_model().objects.get(pk=inviting_pk)\n        employment = Employment.objects.get(pk=employment_pk) if int(employment_pk) != 0 else None\n    except ObjectDoesNotExist:\n        bad_link = True\n\n    try:\n        TimestampSigner().unsign(':'.join([user.email, token_date, token]))\n    except BadSignature:\n        bad_link = True\n\n    if user and user.is_active:\n        if employment and employment.is_approved:\n            # User is active and employment is approved, so nothing to do here\n            return login_and_redirect(request, user)\n        elif employment and not bad_link:\n            # Employment is not yet approved, and link is ok.\n            # Approve employment and log user in.\n            approve_employment(inviting_user, user, employment)\n            return login_and_redirect(request, user)\n        elif employment is None and not bad_link and request.GET.get('project_invite') is not None:\n            return login_and_redirect(request, user)\n\n    if request.method == 'POST':\n        form = InvitedUserForm(user=user, data=request.POST)\n        if form.is_valid():\n            # Approve employment and save new user details\n            form.save(request)\n            if employment is not None:\n                approve_employment(inviting_user, user, employment)\n            return login_and_redirect(request, user)\n    else:\n        form = InvitedUserForm(user=user)\n\n    context = {\n        'form': form,\n        'bad_link': bad_link,\n        'password_length': settings.PASSWORD_MINIMUM_LENGTH\n    }\n    return render(request, 'registration/invite_activate.html', context)\n\n\ndef sign_in(request):\n    \"\"\"Sign in.\n\n    POST have two variants with username & email:\n     - username > normal sign in\n     - email > password reset workflow\n    \"\"\"\n    form = AuthenticationForm()\n    reset_form = PasswordResetForm()\n    if request.method == \"POST\" and 'username' in request.POST:\n        form = AuthenticationForm(data=request.POST)\n        if form.is_valid():\n            login(request, form.get_user())\n            next_page = request.GET.get('next')\n            return HttpResponseRedirect(next_page) if next_page else redirect('my_projects')\n    # Password reset on sign in page\n    elif request.method == \"POST\" and 'email' in request.POST:\n        reset_form = PasswordResetForm(data=request.POST)\n        if reset_form.is_valid():\n            reset_form.save(domain_override=settings.RSR_DOMAIN)\n        return HttpResponse()\n    return render(request, 'sign_in.html', {'form': form, 'reset_form': reset_form})\n\n\ndef sign_out(request):\n    \"\"\"Log out resouce.\"\"\"\n    logout(request)\n    return redirect('index')\n\n\ndef api_key_xml_response(user, orgs):\n    \"\"\"Build the XML response.\n\n    This is used by the Up app - so make sure they match on change.\n    \"\"\"\n    xml_root = etree.Element(\"credentials\")\n\n    # User\n    user_id_element = etree.SubElement(xml_root, \"user_id\")\n    user_id_element.text = str(user.id)\n    user_username_element = etree.SubElement(xml_root, \"username\")\n    user_username_element.text = user.username\n\n    # Organisations\n    for org in orgs:\n        org_id_element = etree.SubElement(xml_root, \"org_id\")\n        org_id_element.text = str(org.id)\n\n    # API key\n    api_key_element = etree.SubElement(xml_root, \"api_key\")\n    api_key_element.text = ApiKey.objects.get_or_create(user=user)[0].key\n\n    # Published and editable projects\n    projects = user.my_projects.published()\n    pub_projs_element = etree.SubElement(xml_root, \"published_projects\")\n    edit_projs_element = etree.SubElement(xml_root, \"allow_edit_projects\")\n    for project in projects:\n        project_id_element = etree.SubElement(pub_projs_element, \"id\")\n        project_id_element.text = str(project.id)\n        if user.has_perm('rsr.change_project', project):\n            project_id_element = etree.SubElement(edit_projs_element, \"id\")\n            project_id_element.text = str(project.id)\n\n    return etree.tostring(etree.ElementTree(xml_root))\n\n\ndef api_key_json_response(user, orgs):\n    \"\"\"\n    Build the JSON response. This is used by the Up app - so make sure they match on change.\n    \"\"\"\n    response_data = dict()\n\n    # User\n    response_data[\"user_id\"] = user.id\n    response_data[\"username\"] = user.username\n\n    # Organisations\n    response_data[\"organisations\"] = [org.id for org in orgs]\n\n    # API key\n    response_data[\"api_key\"] = ApiKey.objects.get_or_create(user=user)[0].key\n\n    # Published projects\n    projects = user.my_projects().published()\n    response_data[\"published_projects\"] = [p.id for p in projects]\n\n    # Editable projects\n    perm = 'rsr.change_project'\n    perm_filter = user.get_permission_filter(perm, '')\n    response_data[\"allow_edit_projects\"] = list(projects.filter(perm_filter).values_list('id', flat=True))\n\n    return json.dumps(response_data)\n\n\n@require_POST\n@csrf_exempt\ndef api_key(request):\n    \"\"\"On successful user credentials returns an auth token for API usage.\n\n    Since RSR changed in v3 to allow users without an organisation we need to\n    introduce a way to make old Up apps work as before but new ones support\n    users without any connected organisations.\n    \"\"\"\n    request_format = request.GET.get('format', 'xml')\n    username = request.POST.get('username', False)\n    password = request.POST.get('password', False)\n    handles_unemployed = bool(request.POST.get(\"handles_unemployed\", False))\n\n    if username and password:\n        try:\n            user = authenticate(username=username, password=password)\n        except ValidationError:\n            user = None\n        if user is not None and user.is_active:\n            orgs = user.approved_organisations()\n            if orgs or handles_unemployed:\n                if request_format == 'xml':\n                    return HttpResponse(api_key_xml_response(user, orgs),\n                                        content_type=\"text/xml\")\n                elif request_format == 'json':\n                    return HttpResponse(api_key_json_response(user, orgs),\n                                        content_type=\"application/json\")\n    return HttpResponseForbidden()\n", "path": "akvo/rsr/views/account.py"}]}