Repository: apluslms/a-plus

Issue:
LTI: remove request.host from LTI parameters
Remove `request.get_host()` from all LTI parameter calculations. Use `settings.BASE_URL` instead. `request.get_host` comes from the browser, thus user can use it to manipulate the data send as part of the post.

These related issues can be fixed in connection or should be created as another issues:

- [x] `tool_consumer_instance_guid` this field doesn't seem to include relevant information. it should be created from settings.BASE_URL
- [x] `launch_presentation_return_url` this should include link to the page in A+, which will render the launch button (basically to the page this link is rendered at).
- [ ] Interesting second part is to include or keep query parameters, so we can request reauthentication by the tool provider (e.g. koodisäilö), which would be sending browser to `{launch_presentation_return_url}?tc_return_url=<ulr>` or such. TODO: raphendyr needs to write down better explanation (i.e. create an issue) - **Moved to an issue #431**


Discussion:
It seems that there is no `SITE_URL` defined in the course settings. Should we use `BASE_URL` instead?
request.get_host() can be only used when the URL is directly visible to the client (web browser). Request.get_host() must not be used for server-to-server communications or protocols. The assignee of the issue should check all uses of request.get_host() in A+ since it has been misused in the past.
Note that the duplicate issue https://github.com/apluslms/a-plus/issues/347 has some additional details.
LTI documentation with the fields and their meanings https://www.imsglobal.org/specs/ltiv1p0/implementation-guide
Is request.get_host() only an issue in the LTI parameters? It is also used in lib/viewbase.py and shibboleth_login/views.py. I don't really understand the problem well enough to know if these are wrong.

Resolving patch:
diff --git a/external_services/lti.py b/external_services/lti.py
--- a/external_services/lti.py
+++ b/external_services/lti.py
@@ -1,4 +1,6 @@
 from hashlib import md5
+from urllib.parse import urlsplit, urljoin
+
 from django.conf import settings
 from django.core.exceptions import PermissionDenied
 from django.utils.translation import get_language
@@ -19,8 +21,9 @@ class LTIRequest(object):
     def __init__(self, service, user, instance, request, title, context_id=None, link_id=None, add=None, exercise=None):
         self.service = service
         course = instance.course
+        base_url_parts = urlsplit(settings.BASE_URL)
         # Context and resource parameters.
-        context_id = context_id or (request.get_host() + instance.get_absolute_url())
+        context_id = context_id or (base_url_parts.netloc + instance.get_absolute_url())
         link_id = link_id or "aplus{:d}".format(service.pk)
         title = title or link_id
 
@@ -60,9 +63,9 @@ def __init__(self, service, user, instance, request, title, context_id=None, lin
             "launch_presentation_locale": get_language(),
             "launch_presentation_document_target":
                 "iframe" if exercise and exercise.open_in_iframe else "window",
-            "launch_presentation_return_url": request.scheme + '://' + request.get_host() + instance.get_absolute_url(),
+            "launch_presentation_return_url": urljoin(settings.BASE_URL, instance.get_absolute_url()),
 
-            "tool_consumer_instance_guid": request.get_host() + "/aplus",
+            "tool_consumer_instance_guid": base_url_parts.netloc + "/aplus",
             "tool_consumer_instance_name": "A+ LMS",
         })
 


Test patch: