-
Notifications
You must be signed in to change notification settings - Fork 2
Release 0.113.2 #2576
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Release 0.113.2 #2576
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,6 +43,8 @@ repos: | |
- compliance/test_data/cybersource/ | ||
- --exclude-files | ||
- ".*_test.js" | ||
- --exclude-files | ||
- "config/keycloak/*" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
- repo: https://github.com/astral-sh/ruff-pre-commit | ||
rev: "v0.7.0" | ||
hooks: | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -119,7 +119,8 @@ | |
"poetry.lock", | ||
"yarn.lock", | ||
"compliance/test_data/cybersource/", | ||
".*_test.js" | ||
".*_test.js", | ||
"config/keycloak/*" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
] | ||
} | ||
], | ||
|
@@ -193,7 +194,7 @@ | |
"filename": "main/settings.py", | ||
"hashed_secret": "09edaaba587f94f60fbb5cee2234507bcb883cc2", | ||
"is_verified": false, | ||
"line_number": 959 | ||
"line_number": 1014 | ||
} | ||
], | ||
"pants": [ | ||
|
@@ -240,5 +241,5 @@ | |
} | ||
] | ||
}, | ||
"generated_at": "2025-03-04T17:10:08Z" | ||
"generated_at": "2025-03-18T15:57:12Z" | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
# MITx Online Keycloak Integration | ||
|
||
The Compose file includes a Keycloak instance that you can use for authentication instead of spinning up a separate one or using one of the deployed instances. It's not enabled by default, but you can run it if you prefer not to run your own Keycloak instance. | ||
|
||
_If you're running the pack-in Keycloak with other apps (Unified Ecommerce, Learn), you might just want to use its instance instead._ There's instructions below on doing that. Start at "Configuring MITx Online" below. | ||
|
||
## Default Settings | ||
|
||
These are the defaults configured for the system. | ||
|
||
### Ports and Hostname | ||
|
||
By default, the Keycloak instance listens on ports `7080` and `7443` and the hostname it expects is `kc.odl.local`. If you want to change this, set these in your `.env` file: | ||
|
||
- `KEYCLOAK_SVC_HOSTNAME` | ||
- `KEYCLOAK_PORT` | ||
- `KEYCLOAK_SSL_PORT` | ||
|
||
### SSL Certificate | ||
|
||
There's a self-signed cert that's in `config/keycloak/tls` - if you'd rather set up your own (or you have a real cert or something to use), you can drop the PEM files in there. See the README there for info. | ||
|
||
### Realm and App Users | ||
|
||
There's a `default-realm.json` in `config/keycloak` that will get loaded by Keycloak when it starts up, and will set up a realm for you with some users and a client so you don't have to set it up yourself. The realm it creates is called `ol-local`. | ||
|
||
The _`ol_local`_ users it sets up are: | ||
|
||
| User | Password | | ||
|---|---| | ||
| `[email protected]` | `student` | | ||
| `[email protected]` | `prof` | | ||
| `[email protected]` | `admin` | | ||
|
||
> These will not get you into the Keycloak admin interface. | ||
|
||
The default realm contains an OIDC client called `apisix`. You can get or change the secrets from within the Keycloak Admin, or you can create a new client if you wish. | ||
|
||
These users are in groups, but the groups don't mean anything by default. | ||
|
||
### Keycloak Admin | ||
|
||
The Keycloak admin interface is at `https://kc.odl.local:7443` by default. As noted, the `ol-local` users above won't get you access to this interface. A separate admin account is configured on first run for this. By default, the credentials are `admin`/`admin` but you can change this by setting these in your `.env`: | ||
- `KEYCLOAK_SVC_ADMIN` | ||
- `KEYCLOAK_SVC_ADMIN_PASSWORD` | ||
|
||
_You probably shouldn't change these, though._ If you want to use a different admin user/password, log into the Keycloak admin after first bringing the container up and create a new user in the Master realm. (There will also be a banner at the top instructing you to do so.) | ||
|
||
## Making it Work | ||
|
||
The Keycloak instance is hidden in the `keycloak` profile in the Composer file, so if you want to interact with it, you'll need to run `docker compose --profile keycloak`, or add `COMPOSE_PROFILES=keycloak` to your `.env` file. (If you start the app without the profile, you can still start Keycloak later by specifying the profile.) | ||
|
||
### Database Config | ||
|
||
If you're **starting from scratch** (no volumes, containers, etc.), the database container should pick up the init script in `config/db` and create a user and database for Keycloak. No further configuration is needed. | ||
|
||
If you're starting with **an existing database**: you will need to create a Keycloak user and database. The easiest way to do it is to just run the `config/db/init-keycloak.sql` script against your running database. | ||
|
||
### First Start | ||
|
||
1. In `config/keycloak/tls`, copy `tls.crt.default` and `tls.key.default` to `tls.crt` and `tls.key`. (Or, you can regenerate them - see the README in that folder.) | ||
2. Set Keycloak environment values in your `.env` file. Most of these are described above, and none are required. | ||
- `KEYCLOAK_SVC_KEYSTORE_PASSWORD` - password for the keystore Keycloak will create | ||
- `KEYCLOAK_SVC_ADMIN` | ||
- `KEYCLOAK_SVC_ADMIN_PASSWORD` | ||
- `KEYCLOAK_SVC_HOSTNAME` | ||
- `KEYCLOAK_PORT` | ||
- `KEYCLOAK_SSL_PORT` | ||
3. Start the Keycloak service: `docker compose --profile keycloak up -d keycloak` | ||
|
||
The Keycloak container should start and stay running. Once it does, you should be able to log in at `https://kc.odl.local:7443/` with username and password `admin` (or the values you supplied). | ||
|
||
## Configuring MITx Online | ||
|
||
To use the Keycloak instance with MITx Online, you need to set these in your `.env` file: | ||
|
||
- `SOCIAL_AUTH_OL_OIDC_OIDC_ENDPOINT` - root endpoint for OIDC for the realm (see below) | ||
- `SOCIAL_AUTH_OL_OIDC_KEY` - client ID - if you're using the defaults, this is `apisix` | ||
- `SOCIAL_AUTH_OL_OIDC_SECRET` - client secret | ||
|
||
These settings can be found in the Keycloak admin. It's easiest to bring Keycloak up on its own via `docker compose up keycloak` to get these values, then bring the rest of the system up later. | ||
|
||
The endpoint URL is available in the Keycloak admin. Open the realm you wish to use - `ol-local` for the pack in one - then navigate to `Realm settings` under Configure. At the bottom of the `General` tab, the URL you want is the `OpenID Endpoint Configuration` link. This will be a URL like `http://kc.odl.local:7080/realms/ol-local/.well-known/openid-configuration` You will need to remove the `.well-known/openid-configuration` from this. | ||
|
||
> The endpoint URL should be the **HTTP** version of this, unless you have a real certificate on your Keycloak instance. Otherwise, you'll get certificate errors. | ||
|
||
> The endpoint URL isn't listed here because you have to go get the secret anyway. Additionally, the endpoint URL can change when new Keycloak versions are released, so it's better to get it out of the admin interface. | ||
|
||
The key and secret are available under `Clients`. The key is the client name (so, by default, `apisix`) and the secret is available under `Credentials` once you open the client configuration. | ||
|
||
### Other Keycloak Instances | ||
|
||
You can use MITx Online with Keycloak instances that aren't the pack-in one - the provided one is just for convenience. Just set the same settings above, but get them from whatever Keycloak instance you already have running. | ||
|
||
_If you're running Keycloak alongside MITx Online on the same machine,_ you will need to perform some additional configuration to make it visible to MITx Online. Docker containers and Compose projects can't generally see each other, and local-only hostnames (e.g. hosts file entries) don't apply inside containers. You can get around this by setting up a composer override file that adds an alias for the hostname you're using for the Keycloak instance to `host-gateway`. There's a sample override file called `docker-compose-keycloak-override.yml` you can use for this. | ||
|
||
### SSO Admins | ||
|
||
If you're using MITx Online with Keycloak, you can technically skip creating a superuser. Instead, you can log in with the account you want to use as a superuser account (e.g. `[email protected]`) and then use the `promote_user` command to add privileges: | ||
|
||
`promote_user --promote --superuser [email protected]` | ||
|
||
This can also be used to demote users or promote only to staff; see its help for more info. The account must exist first so the user must have logged in before you can run the command. | ||
|
||
## Troubleshooting | ||
|
||
A few things to check if you run into issues: | ||
|
||
- _Redirect loop_: Your sessions have gotten into a weird state. Clear cookies for anything at `odl.local` (or whatever domain you're using). | ||
- _Slowness when loading_: (macOS especially) Make sure your `/etc/hosts` contains equivalent entries for `::1` (IPv6 loopback) for your local hostnames. (macOS will generally prefer IPv6 over IPv4.) | ||
- _Errors about discovery_: Make sure the discovery URL (`SOCIAL_AUTH_OL_OIDC_OIDC_ENDPOINT`) is correct. If Keycloak has updated, it may not be. Additionally, make sure you're connecting to the right ports - if you've changed them, the discovery URL may be somewhere else now. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
"""Keycloak Authentication Configuration""" | ||
|
||
import logging | ||
|
||
from social_core.backends.open_id_connect import OpenIdConnectAuth | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
||
class OlOpenIdConnectAuth(OpenIdConnectAuth): | ||
""" | ||
Custom wrapper class for adding additional functionality to the | ||
OpenIdConnectAuth child class. | ||
""" | ||
|
||
name = "ol-oidc" | ||
REQUIRES_EMAIL_VALIDATION = False | ||
|
||
def get_user_details(self, response): | ||
"""Get the user details from the API response""" | ||
details = super().get_user_details(response) | ||
|
||
return { | ||
**details, | ||
"global_id": response.get("sub", None), | ||
"name": response.get("name", None), | ||
"is_active": True, | ||
"profile": { | ||
"name": response.get("name", ""), | ||
"email_optin": bool(int(response["email_optin"])) | ||
if "email_optin" in response | ||
else None, | ||
}, | ||
} | ||
|
||
def __str__(self): | ||
return "OL OpenID Connect (ol-oidc)" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,10 +7,13 @@ | |
from social_core.backends.email import EmailAuth | ||
from social_core.exceptions import AuthException | ||
from social_core.pipeline.partial import partial | ||
from social_core.pipeline.user import create_user | ||
|
||
from authentication.backends.ol_open_id_connect import OlOpenIdConnectAuth | ||
from authentication.exceptions import ( | ||
EmailBlockedException, | ||
InvalidPasswordException, | ||
RequireEmailException, | ||
RequirePasswordAndPersonalInfoException, | ||
RequirePasswordException, | ||
RequireRegistrationException, | ||
|
@@ -30,6 +33,55 @@ | |
# pylint: disable=keyword-arg-before-vararg | ||
|
||
|
||
def forbid_hijack(strategy, backend, **kwargs): # pylint: disable=unused-argument # noqa: ARG001 | ||
""" | ||
Forbid an admin user from trying to login/register while hijacking another user | ||
|
||
Args: | ||
strategy (social_django.strategy.DjangoStrategy): the strategy used to authenticate | ||
backend (social_core.backends.base.BaseAuth): the backend being used to authenticate | ||
""" | ||
# As first step in pipeline, stop a hijacking admin from going any further | ||
if bool(strategy.session_get("hijack_history")): | ||
raise AuthException("You are hijacking another user, don't try to login again") # noqa: EM101 | ||
return {} | ||
|
||
|
||
# Pipeline steps for OIDC logins | ||
|
||
|
||
def create_ol_oidc_user(strategy, details, backend, user=None, *args, **kwargs): | ||
""" | ||
Create the user if we're using the ol-oidc backend. | ||
|
||
This also does a blocked user check and makes sure there's an email address. | ||
If the created user is new, we make sure they're set active. (If the user is | ||
inactive, they'll get knocked out of the pipeline elsewhere.) | ||
""" | ||
|
||
if backend.name != OlOpenIdConnectAuth.name: | ||
return {} | ||
|
||
if "email" not in details: | ||
raise RequireEmailException(backend, None) | ||
|
||
if "email" in details and is_user_email_blocked(details["email"]): | ||
raise EmailBlockedException(backend, None) | ||
|
||
retval = create_user(strategy, details, backend, user, *args, **kwargs) | ||
|
||
# When we have deprecated direct login, remove this and default the is_active | ||
# flag to True in the User model. | ||
if retval.get("is_new"): | ||
retval["user"].is_active = True | ||
retval["user"].save() | ||
Comment on lines
+75
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
return retval | ||
|
||
|
||
# Pipeline steps for email logins | ||
|
||
|
||
def validate_email_auth_request(strategy, backend, user=None, *args, **kwargs): # pylint: disable=unused-argument # noqa: ARG001 | ||
""" | ||
Validates an auth request for email | ||
|
@@ -49,7 +101,7 @@ def validate_email_auth_request(strategy, backend, user=None, *args, **kwargs): | |
return {} | ||
|
||
|
||
def get_username(strategy, backend, user=None, *args, **kwargs): # pylint: disable=unused-argument # noqa: ARG001 | ||
def get_username(strategy, backend, user=None, details=None, *args, **kwargs): # pylint: disable=unused-argument # noqa: ARG001 | ||
""" | ||
Gets the username for a user | ||
|
||
|
@@ -58,6 +110,10 @@ def get_username(strategy, backend, user=None, *args, **kwargs): # pylint: disa | |
backend (social_core.backends.base.BaseAuth): the backend being used to authenticate | ||
user (User): the current user | ||
""" | ||
|
||
if backend and backend.name == OlOpenIdConnectAuth.name: | ||
return {"username": details["username"] if not user else user.username} | ||
|
||
return {"username": None if not user else strategy.storage.user.get_username(user)} | ||
|
||
|
||
|
@@ -209,20 +265,6 @@ def validate_password( | |
return {} | ||
|
||
|
||
def forbid_hijack(strategy, backend, **kwargs): # pylint: disable=unused-argument # noqa: ARG001 | ||
""" | ||
Forbid an admin user from trying to login/register while hijacking another user | ||
|
||
Args: | ||
strategy (social_django.strategy.DjangoStrategy): the strategy used to authenticate | ||
backend (social_core.backends.base.BaseAuth): the backend being used to authenticate | ||
""" | ||
# As first step in pipeline, stop a hijacking admin from going any further | ||
if bool(strategy.session_get("hijack_history")): | ||
raise AuthException("You are hijacking another user, don't try to login again") # noqa: EM101 | ||
return {} | ||
|
||
|
||
def create_openedx_user(strategy, backend, user=None, is_new=False, **kwargs): # pylint: disable=unused-argument # noqa: FBT002, ARG001 | ||
""" | ||
Create a user in the openedx, deferring a retry via celery if it fails | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
KEYCLOAK_SVC_KEYSTORE_PASSWORD
is set tosupersecret123456789
by default. This is a major security risk, as anyone deploying the application without changing this password will be vulnerable. Consider removing the default value or generating a random one during deployment. It is recommended to not have a default value at all, forcing the user to set one.