Skip to content
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

Add generic OIDC login option #10614

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added .settings.dist.py.sha256sum
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ to be created. Closely follow the steps below to guarantee success.
DD_SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS = ['example.com', 'example.org']
{{< /highlight >}}

As an environment variable:
As an environment variable:

{{< highlight python >}}
DD_SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS = example.com,example.org
Expand All @@ -98,7 +98,7 @@ to be created. Closely follow the steps below to guarantee success.
DD_SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_EMAILS = ['<[email protected]>']
{{< /highlight >}}

As an environment variable:
As an environment variable:

{{< highlight python >}}
DD_SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_EMAILS = [email protected],[email protected]
Expand Down Expand Up @@ -203,7 +203,7 @@ user, such as 'superuser'.
button on the login page which should *magically* work

### Automatic Import of User-Groups
To import groups from Azure AD users, the following environment variable needs to be set:
To import groups from Azure AD users, the following environment variable needs to be set:

{{< highlight python >}}
DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GET_GROUPS=True
Expand All @@ -212,16 +212,16 @@ To import groups from Azure AD users, the following environment variable needs t
This will ensure the user is added to all the groups found in the Azure AD Token. Any missing groups will be created in DefectDojo (unless filtered). This group synchronization allows for product access via groups to limit the products a user can interact with.

The Azure AD token returned by Azure will also need to be configured to include group IDs. Without this step, the
token will not contain any notion of a group, and the mapping process will report that the current user is not a member of any
token will not contain any notion of a group, and the mapping process will report that the current user is not a member of any
groups. To update the the format of the token, add a group claim that applies to whatever group type you are using.
If unsure of what type that is, select `All Groups`. Do not activate `Emit groups as role claims` within the Azure AD
If unsure of what type that is, select `All Groups`. Do not activate `Emit groups as role claims` within the Azure AD
"Token configuration" page.

Application API permissions need to be updated with the `Group.Read.All` permission so that groups can be read on behalf
of the user that has successfully signed in.

To limit the amount of groups imported from Azure AD, a regular expression can be used as the following:

{{< highlight python >}}
DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GROUPS_FILTER='^team-.*' # or 'teamA|teamB|groupC'
{{< /highlight >}}
Expand Down Expand Up @@ -274,15 +274,15 @@ Follow along below.
{{< /highlight >}}

**Important:** if you enable this setting on already working instance with gitlab integrations, it will require new grant "read_repository" by user

5. Restart DefectDojo, and you should now see a **Login with Gitlab**
button on the login page.

## Keycloak
There is also an option to use Keycloak as OAuth2 provider in order to authenticate users to Defect Dojo, also by using
the social-auth plugin.

Here are suggestion on how to configure Keycloak and DefectDojo:
Here are suggestion on how to configure Keycloak and DefectDojo:

### Configure Keycloak
(assuming you already have an existing realm, otherwise create one)
Expand All @@ -295,7 +295,7 @@ Here are suggestion on how to configure Keycloak and DefectDojo:
* Under `Fine grained openID connect configuration` -> `request object signature algorithm`: set to `RS256`
* -> save these settings in keycloak (hit save button)
3. Under `Scope` -> `Full Scope Allowed` set to `off`
4. Under `mappers` -> add a custom mapper here:
4. Under `mappers` -> add a custom mapper here:
* Name: `aud`
* Mapper type: `audience`
* Included audience: select your client/client-id here
Expand All @@ -306,6 +306,35 @@ Here are suggestion on how to configure Keycloak and DefectDojo:
7. In your realm settings -> general -> endpoints: look into openId endpoint configuration
and look up your authorization and token endpoint (use them below)

### Configure OIDC
Provides the option to authenticate users using a generic OIDC provider.

The minimum configuration requires:

{{< highlight python >}}
DD_SOCIAL_AUTH_OIDC_AUTH_ENABLED=True,
DD_SOCIAL_AUTH_OIDC_OIDC_ENDPOINT=(str, 'https://example.com'),
DD_SOCIAL_AUTH_OIDC_KEY=(str, 'YOUR_CLIENT_ID'),
DD_SOCIAL_AUTH_OIDC_SECRET=(str, 'YOUR_CLIENT_SECRET')
{{< /highlight >}}

The rest of the OIDC configuration will be auto-detected by fetching data from:
- <DD_SOCIAL_AUTH_OIDC_OIDC_ENDPOINT>/.well-known/open-id-configuration/

You can also optionally set the following:

{{< highlight python >}}
DD_SOCIAL_AUTH_OIDC_ID_KEY=(str, ''), #the key associated with the OIDC user IDs
DD_SOCIAL_AUTH_OIDC_USERNAME_KEY=(str, ''), #the key associated with the OIDC usernames
DD_SOCIAL_AUTH_OIDC_WHITELISTED_DOMAINS=(list, ['']), #list of domains allowed for login
DD_SOCIAL_AUTH_OIDC_JWT_ALGORITHMS=(list, ["RS256","HS256"]),
DD_SOCIAL_AUTH_OIDC_ID_TOKEN_ISSUER=(str, ''),
DD_SOCIAL_AUTH_OIDC_ACCESS_TOKEN_URL=(str, ''),
DD_SOCIAL_AUTH_OIDC_AUTHORIZATION_URL=(str, ''),
DD_SOCIAL_AUTH_OIDC_USERINFO_URL=(str, ''),
DD_SOCIAL_AUTH_OIDC_JWKS_URI=(str, ''),
{{< /highlight >}}

### Configure Defect Dojo
Edit the settings (see [Configuration](../../os_getting_started/configuration)) with the following
information:
Expand All @@ -316,13 +345,13 @@ Edit the settings (see [Configuration](../../os_getting_started/configuration))
DD_SECURE_SSL_REDIRECT=True,
DD_SOCIAL_AUTH_KEYCLOAK_OAUTH2_ENABLED=True,
DD_SOCIAL_AUTH_KEYCLOAK_PUBLIC_KEY=(str, '<your realm public key>'),
DD_SOCIAL_AUTH_KEYCLOAK_KEY=(str, '<your client id>'),
DD_SOCIAL_AUTH_KEYCLOAK_SECRET=(str, '<your keycloak client credentials secret>'),
DD_SOCIAL_AUTH_KEYCLOAK_KEY=(str, '<your client id>'),
DD_SOCIAL_AUTH_KEYCLOAK_SECRET=(str, '<your keycloak client credentials secret>'),
DD_SOCIAL_AUTH_KEYCLOAK_AUTHORIZATION_URL=(str, '<your authorization endpoint>'),
DD_SOCIAL_AUTH_KEYCLOAK_ACCESS_TOKEN_URL=(str, '<your token endpoint>')
DD_SOCIAL_AUTH_KEYCLOAK_ACCESS_TOKEN_URL=(str, '<your token endpoint>')
{{< /highlight >}}
or, alternatively, for helm configuration, add this to the `extraConfig` section:

or, alternatively, for helm configuration, add this to the `extraConfig` section:

```yaml
DD_SESSION_COOKIE_SECURE: 'True'
Expand All @@ -336,7 +365,7 @@ DD_SOCIAL_AUTH_KEYCLOAK_AUTHORIZATION_URL: '<your authorization endpoint>'
DD_SOCIAL_AUTH_KEYCLOAK_ACCESS_TOKEN_URL: '<your token endpoint>'
```

Optionally, you *can* set `DD_SOCIAL_AUTH_KEYCLOAK_LOGIN_BUTTON_TEXT` in order to customize the login button's text caption.
Optionally, you *can* set `DD_SOCIAL_AUTH_KEYCLOAK_LOGIN_BUTTON_TEXT` in order to customize the login button's text caption.

## GitHub Enterprise
1. Navigate to your GitHub Enterprise Server and follow instructions to create a new OAuth App [https://docs.github.com/en/enterprise-server/developers/apps/building-oauth-apps/creating-an-oauth-app](https://docs.github.com/en/enterprise-server/developers/apps/building-oauth-apps/creating-an-oauth-app)
Expand All @@ -346,20 +375,20 @@ Optionally, you *can* set `DD_SOCIAL_AUTH_KEYCLOAK_LOGIN_BUTTON_TEXT` in order t
- **https://the_hostname_you_have_dojo_deployed:your_server_port/complete/github-enterprise/**
4. Edit the settings (see [Configuration](../../os_getting_started/configuration)) with the following
information:
{{< highlight python >}}
DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY=(str, 'GitHub Enterprise OAuth App Client ID'),
DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_SECRET=(str, 'GitHub Enterprise OAuth App Client Secret'),
DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_URL=(str, 'https://github.<your_company>.com/'),
DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL=(str, 'https://github.<your_company>.com/api/v3/'),
DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_OAUTH2_ENABLED = True,
{{< highlight python >}}
DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY=(str, 'GitHub Enterprise OAuth App Client ID'),
DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_SECRET=(str, 'GitHub Enterprise OAuth App Client Secret'),
DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_URL=(str, 'https://github.<your_company>.com/'),
DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL=(str, 'https://github.<your_company>.com/api/v3/'),
DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_OAUTH2_ENABLED = True,
{{< /highlight >}}
5. Restart DefectDojo, and you should now see a **Login with GitHub Enterprise**
button on the login page.
button on the login page.

## SAML 2.0
In a similar direction to OAuth, this SAML addition provides a more secure
perogative to SSO. For definitions of terms used and more information,
see the plugin [plugin homepage](https://github.com/IdentityPython/djangosaml2).
see the plugin [plugin homepage](https://github.com/IdentityPython/djangosaml2).

1. Navigate to your SAML IdP and find your metadata
2. Edit the settings (see [Configuration](../../os_getting_started/configuration)) with the following
Expand Down
1 change: 1 addition & 0 deletions dojo/context_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ def globalize_vars(request):
"FORGOT_PASSWORD": settings.FORGOT_PASSWORD,
"FORGOT_USERNAME": settings.FORGOT_USERNAME,
"CLASSIC_AUTH_ENABLED": settings.CLASSIC_AUTH_ENABLED,
"OIDC_ENABLED": settings.OIDC_AUTH_ENABLED,
"AUTH0_ENABLED": settings.AUTH0_OAUTH2_ENABLED,
"GOOGLE_ENABLED": settings.GOOGLE_OAUTH_ENABLED,
"OKTA_ENABLED": settings.OKTA_OAUTH_ENABLED,
Expand Down
29 changes: 29 additions & 0 deletions dojo/settings/settings.dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,19 @@
DD_SOCIAL_AUTH_CREATE_USER=(bool, True), # if True creates user at first login
DD_SOCIAL_LOGIN_AUTO_REDIRECT=(bool, False), # auto-redirect if there is only one social login method
DD_SOCIAL_AUTH_TRAILING_SLASH=(bool, True),
DD_SOCIAL_AUTH_OIDC_AUTH_ENABLED=(bool, False),
DD_SOCIAL_AUTH_OIDC_OIDC_ENDPOINT=(str, ""),
DD_SOCIAL_AUTH_OIDC_ID_KEY=(str, ""),
DD_SOCIAL_AUTH_OIDC_KEY=(str, ""),
DD_SOCIAL_AUTH_OIDC_SECRET=(str, ""),
DD_SOCIAL_AUTH_OIDC_USERNAME_KEY=(str, ""),
DD_SOCIAL_AUTH_OIDC_WHITELISTED_DOMAINS=(list, [""]),
DD_SOCIAL_AUTH_OIDC_JWT_ALGORITHMS=(list, ["RS256", "HS256"]),
DD_SOCIAL_AUTH_OIDC_ID_TOKEN_ISSUER=(str, ""),
DD_SOCIAL_AUTH_OIDC_ACCESS_TOKEN_URL=(str, ""),
DD_SOCIAL_AUTH_OIDC_AUTHORIZATION_URL=(str, ""),
DD_SOCIAL_AUTH_OIDC_USERINFO_URL=(str, ""),
DD_SOCIAL_AUTH_OIDC_JWKS_URI=(str, ""),
DD_SOCIAL_AUTH_AUTH0_OAUTH2_ENABLED=(bool, False),
DD_SOCIAL_AUTH_AUTH0_KEY=(str, ""),
DD_SOCIAL_AUTH_AUTH0_SECRET=(str, ""),
Expand Down Expand Up @@ -484,6 +497,7 @@ def generate_url(scheme, double_slashes, user, password, host, port, path, param

# These are the individidual modules supported by social-auth
AUTHENTICATION_BACKENDS = (
"social_core.backends.open_id_connect.OpenIdConnectAuth",
"social_core.backends.auth0.Auth0OAuth2",
"social_core.backends.google.GoogleOAuth2",
"social_core.backends.okta.OktaOAuth2",
Expand Down Expand Up @@ -576,6 +590,20 @@ def generate_url(scheme, double_slashes, user, password, host, port, path, param
if GITLAB_PROJECT_AUTO_IMPORT:
SOCIAL_AUTH_GITLAB_SCOPE += ["read_repository"]

OIDC_AUTH_ENABLED = env("DD_SOCIAL_AUTH_OIDC_AUTH_ENABLED")
SOCIAL_AUTH_OIDC_OIDC_ENDPOINT = env("DD_SOCIAL_AUTH_OIDC_OIDC_ENDPOINT")
SOCIAL_AUTH_OIDC_ID_KEY = env("DD_SOCIAL_AUTH_OIDC_ID_KEY")
SOCIAL_AUTH_OIDC_KEY = env("DD_SOCIAL_AUTH_OIDC_KEY")
SOCIAL_AUTH_OIDC_SECRET = env("DD_SOCIAL_AUTH_OIDC_SECRET")
SOCIAL_AUTH_OIDC_USERNAME_KEY = env("DD_SOCIAL_AUTH_OIDC_USERNAME_KEY")
SOCIAL_AUTH_OIDC_WHITELISTED_DOMAINS = env("DD_SOCIAL_AUTH_OIDC_WHITELISTED_DOMAINS")
SOCIAL_AUTH_OIDC_JWT_ALGORITHMS = env("DD_SOCIAL_AUTH_OIDC_JWT_ALGORITHMS")
SOCIAL_AUTH_OIDC_ID_TOKEN_ISSUER = env("DD_SOCIAL_AUTH_OIDC_ID_TOKEN_ISSUER")
SOCIAL_AUTH_OIDC_ACCESS_TOKEN_URL = env("DD_SOCIAL_AUTH_OIDC_ACCESS_TOKEN_URL")
SOCIAL_AUTH_OIDC_AUTHORIZATION_URL = env("DD_SOCIAL_AUTH_OIDC_AUTHORIZATION_URL")
SOCIAL_AUTH_OIDC_USERINFO_URL = env("DD_SOCIAL_AUTH_OIDC_USERINFO_URL")
SOCIAL_AUTH_OIDC_JWKS_URI = env("DD_SOCIAL_AUTH_OIDC_JWKS_URI")

AUTH0_OAUTH2_ENABLED = env("DD_SOCIAL_AUTH_AUTH0_OAUTH2_ENABLED")
SOCIAL_AUTH_AUTH0_KEY = env("DD_SOCIAL_AUTH_AUTH0_KEY")
SOCIAL_AUTH_AUTH0_SECRET = env("DD_SOCIAL_AUTH_AUTH0_SECRET")
Expand Down Expand Up @@ -628,6 +656,7 @@ def generate_url(scheme, double_slashes, user, password, host, port, path, param
rf"^{URL_PREFIX}api/v2/",
r"complete/",
r"empty_questionnaire/([\d]+)/answer",
r"oauth2/idpresponse",
rf"^{URL_PREFIX}password_reset/",
rf"^{URL_PREFIX}forgot_username",
rf"^{URL_PREFIX}reset/",
Expand Down
6 changes: 6 additions & 0 deletions dojo/templates/dojo/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ <h3>{% trans "Login" %}</h3>
{% endif %}
</div>
<div class="form-group">
{% if OIDC_ENABLED is True %}
<div class="col-sm-offset-1 col-sm-2">
<a href="{% url 'social:begin' 'oidc' %}?next={{ request.GET.next }}" style="color: rgb(255, 255, 255)" class="btn btn-success" type="button">{% trans "Login with OIDC" %}</a>
</div>
{% endif %}

{% if GOOGLE_ENABLED is True %}
<div class="col-sm-offset-1 col-sm-2">
<a href="{% url 'social:begin' 'google-oauth2' %}?next={{ request.GET.next }}" style="color: rgb(255,255,255)" class="btn btn-success" type="button">{% trans "Login with Google" %}</a>
Expand Down
5 changes: 4 additions & 1 deletion dojo/user/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
settings.AUTH0_OAUTH2_ENABLED,
settings.KEYCLOAK_OAUTH2_ENABLED,
settings.GITHUB_ENTERPRISE_OAUTH2_ENABLED,
settings.OIDC_AUTH_ENABLED,
settings.SAML2_ENABLED,
]) == 1 and "force_login_form" not in request.GET:
if settings.GOOGLE_OAUTH_ENABLED:
Expand All @@ -138,6 +139,8 @@
social_auth = "gitlab"
elif settings.KEYCLOAK_OAUTH2_ENABLED:
social_auth = "keycloak"
elif settings.OIDC_AUTH_ENABLED:
social_auth = "oidc"
elif settings.AUTH0_OAUTH2_ENABLED:
social_auth = "auth0"
elif settings.GITHUB_ENTERPRISE_OAUTH2_ENABLED:
Expand Down Expand Up @@ -647,7 +650,7 @@
connection.open()
connection.close()
except Exception as e:
logger.error(f"SMTP Server Connection Failure: {e}")
logger.error(f"SMTP Server Connection Failure: {str(e)}")

Check failure on line 653 in dojo/user/views.py

View workflow job for this annotation

GitHub Actions / ruff-linting

Ruff (RUF010)

dojo/user/views.py:653:61: RUF010 Use explicit conversion flag
msg = "SMTP server is not configured correctly..."
raise ValidationError(msg)

Expand Down
Loading