There is one thing for which I would really like “documentation” when we will be testing Juniper.
How do we create the different JWT ?
What values do we put where in lms.env.json, cms.env.json or anywhere else ?
A written almost real-life example would definitely benefit me or other operators in the Open edX community.
Or maybe it is simply me not understanding how to create the tokens and where each value created fits in the different JSON files. I hope I am not alone in this situation…
I should probably add some background.
I recently tried installing a version of “master” for Open edX. I needed to test something without our fork.
Normally, at least with ironwood and previous open-releases, after installation I can login as
[email protected]
,
[email protected]
and a few other users. Now, I can’t…
Looking at /edx/var/log/lms/edx.log I see the following error messages whenever I try to log in with these users:
Nov 18 17:15:09 ip-10-0-0-208 [service_variant=lms][django.request][env:sandbox] ERROR [ip-10-0-0-208 19883] [user None] [exception.py:135] - Internal Server Error: /user_api/v1/account/login_session/
Traceback (most recent call last):
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/core/handlers/exception.py", line 41, in inner
response = get_response(request)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 249, in _legacy_get_response
response = self._get_response(request)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 187, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 185, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/utils/decorators.py", line 185, in inner
return func(*args, **kwargs)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
return view_func(*args, **kwargs)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/views/generic/base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/utils/decorators.py", line 67, in _wrapper
return bound_func(*args, **kwargs)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/views/decorators/debug.py", line 76, in sensitive_post_parameters_wrapper
return view(request, *args, **kwargs)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/utils/decorators.py", line 63, in bound_func
return func.__get__(self, type(self))(*args2, **kwargs2)
File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/user_authn/views/login.py", line 433, in dispatch
return super(LoginSessionView, self).dispatch(request, *args, **kwargs)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/rest_framework/views.py", line 494, in dispatch
response = self.handle_exception(exc)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/rest_framework/views.py", line 454, in handle_exception
self.raise_uncaught_exception(exc)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/rest_framework/views.py", line 491, in dispatch
response = handler(request, *args, **kwargs)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/utils/decorators.py", line 67, in _wrapper
return bound_func(*args, **kwargs)
File "/edx/app/edxapp/edx-platform/openedx/core/lib/api/view_utils.py", line 371, in _wrapped
return func(request)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/utils/decorators.py", line 63, in bound_func
return func.__get__(self, type(self))(*args2, **kwargs2)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/utils/decorators.py", line 67, in _wrapper
return bound_func(*args, **kwargs)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/utils/decorators.py", line 149, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/utils/decorators.py", line 63, in bound_func
return func.__get__(self, type(self))(*args2, **kwargs2)
File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/user_authn/views/login.py", line 429, in post
return shim_student_view(login_user, check_logged_in=True)(request)
File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/user_authn/views/login.py", line 501, in _inner
response = view_func(request)
File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/utils/decorators.py", line 149, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/user_authn/views/login.py", line 362, in login_user
return set_logged_in_cookies(request, response, possibly_authenticated_user)
File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/user_authn/cookies.py", line 151, in set_logged_in_cookies
_create_and_set_jwt_cookies(response, request, cookie_settings, user=user)
File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/user_authn/cookies.py", line 260, in _create_and_set_jwt_cookies
jwt = _create_jwt(request, user, expires_in)
File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/user_authn/cookies.py", line 280, in _create_jwt
return create_jwt_from_token(access_token, DOTAdapter(), use_asymmetric_key=True)
File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/oauth_dispatch/jwt.py", line 73, in create_jwt_from_token
filters=oauth_adapter.get_authorization_filters(client),
File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/oauth_dispatch/jwt.py", line 134, in _create_jwt
return _encode_and_sign(payload, use_asymmetric_key, secret)
File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/oauth_dispatch/jwt.py", line 214, in _encode_and_sign
serialized_keypair = json.loads(settings.JWT_AUTH['JWT_PRIVATE_SIGNING_JWK'])
File "/usr/lib/python2.7/json/__init__.py", line 339, in loads
return _default_decoder.decode(s)
File "/usr/lib/python2.7/json/decoder.py", line 364, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib/python2.7/json/decoder.py", line 382, in raw_decode
raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded
It seems there is a problem with the default values in /edx/app/edxapp/lms.env.json and /edx/app/edxapp/cms.env.json and I would really like to put correct values so that I could log in.
Any ideas?
Juniper Alpha basic installation
I might have a follow-up questions with regards to /edx/etc/lms.yml and /edx/app/edxapp/lms.env.json, but for now I was able to add the correct values in /edx/etc/lms.yml.
I first had to run the following commands:
sudo -H -u edxapp bash
source edxapp_env
source venvs/edxapp/bin/activate
cd edx-platform
python manage.py lms generate_jwt_signing_key
The last command generates values for JWT_PUBLIC_SIGNING_JWK_SET, JWT_PRIVATE_SIGNING_JWK and JWT_SIGNING_ALGORITHM that needs to be filled in into /edx/etc/lms.yml.
And after restarting the LMS and the CMS, I was able to login again with my regular user in the LMS and the CMS.
I don’t know, if this is the “official” solution but it did work for me.
I will investigate whether there is a better answer than this.
Yes, that is an appropriate solution. That management command generates the keys that you need to sign and verify JWTs. For security reasons, we need each installation to generate its own set of keys so we do not ship with default values.
Is there a place in the Juniper installation process where running the management command and copying over the keys can be automated? Or can automation of this be tackled by the build-test-release working group?
There is a generate-passwords.sh step in the Native installation. We can add more to it.
github.com
#!/usr/bin/env bash
# Read a list of Ansible variables that should have generated values, and make
# a new file just like it, with the generated values.
TARGET=${CONFIGURATION_VERSION-${OPENEDX_RELEASE-master}}
wget -q "https://raw.githubusercontent.com/edx/configuration/$TARGET/playbooks/sample_vars/passwords.yml" -O passwords-template.yml
while IFS= read -r line; do
# Make a random string. SECRET_KEY's should be longer.
length=35
if [[ $line == *SECRET_KEY* ]]; then
length=100
REPLACE=$(LC_ALL=C < /dev/urandom tr -dc 'A-Za-z0-9' | head -c$length)
# Change "!!null"-to-end-of-line to the password.
echo "$line" | sed "s/\!\!null.*/\'$REPLACE\'/"
done < passwords-template.yml > my-passwords.yml
Looks like the docs for this question have been added to the Juniper Confluence under Django Configuration and SetUp thank you!
@sambapete Can this post be marked SOLVED and CRI-173 closed?
I am in favour of both closing it and automating it in the Native install. I am afraid that if it isn’t automated people will ask again and again and again why they can’t login to their site anymore or where to find the instructions. Not everyone has the same technical background as you @nedbat or @jill or me @sambapete
Just my 2 canadian cents. If found a piggy bank full of them while cleaning up my apartment recently. We do not have a cent currency in Canada for the last few years.