Skip to content

Commit 70163d1

Browse files
authored
Merge pull request #4650 from mathesar-foundation/release-0.4.0
Release 0.4.0
2 parents 3ec6234 + 16479f2 commit 70163d1

File tree

164 files changed

+4119
-1177
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

164 files changed

+4119
-1177
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
ALLOWED_HOSTS=".localhost, 127.0.0.1, [::1]"
22
SECRET_KEY="2gr6ud88x=(p855_5nbj_+7^bw-iz&n7ldqv%94mjaecl+b9=4"
3+
OIDC_CONFIG_DICT="{\"version\": 1,\"oidc_providers\": { \"provider1\": { \"provider_name\": \"okta\", \"client_id\": \"client-id\", \"secret\": null, \"server_url\": \"https://trial-2872264-admin.okta.com\" }}}"

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ labels: "type: bug, needs: triage"
1414
<!-- How can we recreate this bug? Please try to provide a Minimal, Complete, and Verifiable (http://stackoverflow.com/help/mcve) example if code-related. -->
1515

1616
## Environment
17+
- Mathesar version: (Visit https://{your-mathesar-install.com}/administration/update/ to find the current version)
1718
- OS: (_eg._ macOS 10.14.6; Fedora 32)
1819
- Browser: (_eg._ Safari; Firefox)
1920
- Browser Version: (_eg._ 13; 73)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
name: "[MAINTAINERS ONLY] Roadmap item"
3+
about: "This issue template is for the convenience of Mathesar maintainers. Please do not use it if you are not a maintainer."
4+
labels: "type: project, needs: requirements"
5+
---
6+
7+
> _This is a meta issue tracking the implementation of this project._
8+
9+
## Outcome
10+
<!-- A clear and concise description of the outcome -->
11+
12+
More detail:
13+
- Requirements TBD
14+
- Project proposal TBD
15+
16+
## Tasks
17+
*Issues to be created soon.*
18+
19+
## Related
20+
<!-- Any related resources, discussions, issues, etc. -->

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ Temporary Items
181181
.media/
182182
docs/_build/
183183

184+
## SSO-SPECIFIC ##
185+
sso.yml
186+
184187
# UI
185188
node_modules/
186189

DEVELOPER_GUIDE.md

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -49,28 +49,9 @@ Before getting started with your code changes, read our [Contributor guide](./CO
4949
5050
## Loading sample data
5151
52-
- Using a CSV File (limited visibility of features):
52+
Mathesar comes with several built-in sample datasets to help you get started quickly with local development or feature evaluation. You can install these directly from the "Create" and "Connect Database" dialogs in the UI. Each dataset is designed to showcase different relationships (many-to-many, many-to-one) in commonplace example scenarios. The **Library Management** dataset is the largest, and is especially useful for performance-related testing.
5353
54-
For sample table data, you can create a new table in the UI using the `patents.csv` file found in `/mathesar/tests/data`.
55-
56-
- Using Mathesar Data Playground (recommended):
57-
58-
1. Clone the `mathesar-data-playground` repo:
59-
```
60-
git clone https://github.com/mathesar-foundation/mathesar-data-playground.git
61-
```
62-
63-
2. Load the data from sql by running:
64-
```
65-
sudo docker exec -i mathesar_dev_db bash -c 'psql -U mathesar' < /path/to/your/cloned/repo/mathesar-data-playground/realistic_library_simulation/simulation_runs/simulation_run_20230106_00.sql
66-
```
67-
```
68-
sudo docker exec -i mathesar_dev_db bash -c 'psql -U mathesar' < /path/to/your/cloned/repo/mathesar-data-playground/realistic_library_simulation/simulation_runs/simulation_run_20230106_00_checkouts.sql
69-
```
70-
3. [Sync]( https://docs.mathesar.org/user-guide/syncing-db/) these changes from the UI.
71-
72-
73-
<!-- TODO add more content about sample data -->
54+
For more example datasets, see the [mathesar-data-playground](https://github.com/mathesar-foundation/mathesar-data-playground) repository. It includes both the preinstalled datasets and additional ones, like a large, high-quality **Movies** dataset. [Installation instructions for the Movies dataset are available here](https://github.com/mathesar-foundation/mathesar-data-playground/tree/master/movie_rentals#how-to-load-this-data-into-mathesar).
7455
7556
## API
7657

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
</p>
1010

1111
<p align="center">
12-
<a href="https://mathesar.org?ref=github-readme" target="_blank">Website</a> • <a href="https://docs.mathesar.org?ref=github-readme-top" target="_blank">Docs</a> • <a href="https://wiki.mathesar.org/en/community/matrix" target="_blank">Matrix (chat)</a> • <a href="https://discord.gg/enaKqGn5xx" target="_blank">Discord</a> • <a href="https://wiki.mathesar.org/" target="_blank">Contributor Wiki</a>
12+
<a href="https://mathesar.org?ref=github-readme" target="_blank">Website</a> • <a href="https://docs.mathesar.org?ref=github-readme-top" target="_blank">Docs</a> • <a href="https://wiki.mathesar.org/en/community/matrix" target="_blank">Matrix (chat)</a> • <a href="https://discord.gg/enaKqGn5xx" target="_blank">Discord</a> • <a href="https://wiki.mathesar.org/" target="_blank">Contributor Wiki</a> • <a href="https://github.com/orgs/mathesar-foundation/projects/2" target="_blank">Roadmap</a>
1313
</p>
1414

1515

config/settings/common_settings.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@
1111
"""
1212

1313
import os
14+
import traceback
1415
from pathlib import Path
1516

17+
import json
18+
import yaml
19+
from collections import defaultdict
1620
from config.database_config import PostgresConfig, parse_port
1721

1822

@@ -34,6 +38,10 @@
3438
"django_property_filter",
3539
"modernrpc",
3640
"mathesar",
41+
"allauth",
42+
"allauth.account",
43+
"allauth.socialaccount",
44+
"allauth.socialaccount.providers.openid_connect",
3745
]
3846

3947
MIDDLEWARE = [
@@ -48,8 +56,98 @@
4856
"django.middleware.clickjacking.XFrameOptionsMiddleware",
4957
"mathesar.middleware.CursorClosedHandlerMiddleware",
5058
"mathesar.middleware.PasswordChangeNeededMiddleware",
59+
"allauth.account.middleware.AccountMiddleware",
5160
]
5261

62+
OIDC_CONFIG_FILE = BASE_DIR.joinpath('sso.yml')
63+
OIDC_CONFIG_DICT = {}
64+
# Try loading OIDC_CONFIG_DICT from env, iff it doesn't exist, try loading from sso.yml
65+
try:
66+
OIDC_CONFIG_DICT = json.loads(os.getenv('OIDC_CONFIG_DICT', "{}"))
67+
except Exception as e:
68+
traceback.print_exception(type(e), e, e.__traceback__)
69+
try:
70+
if OIDC_CONFIG_DICT in [None, {}] and OIDC_CONFIG_FILE.exists():
71+
with open(OIDC_CONFIG_FILE, "rb") as f:
72+
OIDC_CONFIG_DICT = yaml.full_load(f)
73+
except Exception as e:
74+
traceback.print_exception(type(e), e, e.__traceback__)
75+
OIDC_CONFIG = []
76+
OIDC_ALLOWED_EMAIL_DOMAINS = {}
77+
OIDC_DEFAULT_PG_ROLE_MAP = defaultdict(list)
78+
79+
try:
80+
for providers, config in OIDC_CONFIG_DICT['oidc_providers'].items():
81+
if not config:
82+
continue
83+
provider_name = config.get("provider_name")
84+
client_id = config.get("client_id")
85+
secret = config.get("secret")
86+
server_url = config.get("server_url")
87+
allowed_email_domains = config.get("allowed_email_domains", []) # Here [] means allow all domains.
88+
default_pg_role = config.get("default_pg_role", {})
89+
if all([provider_name, client_id, secret, server_url]):
90+
OIDC_CONFIG.append(
91+
{
92+
"provider_id": provider_name.lower(),
93+
"name": provider_name.lower(),
94+
"client_id": client_id,
95+
"secret": secret,
96+
"settings": {
97+
"server_url": server_url
98+
}
99+
}
100+
)
101+
102+
if isinstance(allowed_email_domains, list):
103+
OIDC_ALLOWED_EMAIL_DOMAINS[provider_name] = allowed_email_domains
104+
elif isinstance(allowed_email_domains, str):
105+
OIDC_ALLOWED_EMAIL_DOMAINS[provider_name] = [allowed_email_domains]
106+
107+
for role_info in default_pg_role.values():
108+
if not role_info:
109+
continue
110+
db_name = role_info.get("name")
111+
host = role_info.get("host")
112+
port = role_info.get("port")
113+
role_name = role_info.get("role")
114+
if all([db_name, host, port, role_name]):
115+
OIDC_DEFAULT_PG_ROLE_MAP[provider_name].append(
116+
{
117+
"db_name": db_name,
118+
"host": host,
119+
"port": port,
120+
"role_name": role_name
121+
}
122+
)
123+
except Exception as e:
124+
# We swallow any exceptions when SSO is misconfigured in sso.yml so that the django server doesn't fail to start.
125+
if OIDC_CONFIG_DICT not in [None, {}]:
126+
traceback.print_exception(type(e), e, e.__traceback__) # Print the traceback in case of an exception.
127+
128+
129+
SOCIALACCOUNT_PROVIDERS = {
130+
"openid_connect": {
131+
"APPS": OIDC_CONFIG
132+
}
133+
}
134+
135+
SOCIALACCOUNT_ADAPTER = "mathesar.oidc.SocialAccountAdapter"
136+
137+
AUTHENTICATION_BACKENDS = [
138+
# Needed to login by username in Django admin, regardless of `allauth`
139+
'django.contrib.auth.backends.ModelBackend',
140+
141+
# `allauth` specific authentication methods, such as login by email
142+
'allauth.account.auth_backends.AuthenticationBackend',
143+
]
144+
145+
# Allows us to merge existing users with OIDC logins.
146+
# More context: https://docs.allauth.org/en/dev/socialaccount/configuration.html
147+
SOCIALACCOUNT_EMAIL_AUTHENTICATION = True
148+
SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT = True
149+
150+
53151
ROOT_URLCONF = "config.urls"
54152

55153
MODERNRPC_METHODS_MODULES = [
@@ -117,6 +215,9 @@
117215
role=POSTGRES_USER,
118216
password=POSTGRES_PASSWORD,
119217
).to_django_dict()
218+
DATABASES['default']['OPTIONS'] = {
219+
"application_name": "Mathesar Django"
220+
}
120221

121222
for db_key, db_dict in DATABASES.items():
122223
# Engine should be '.postgresql' or '.postgresql_psycopg2' for all db(s)

db/connection.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import psycopg
12
from psycopg.rows import dict_row
23
from uuid import uuid4
34

@@ -58,3 +59,14 @@ def select_from_msar_func(conn, func_name, *args):
5859
def load_file_with_conn(conn, file_handle):
5960
"""Run an SQL script from a file, using psycopg."""
6061
conn.execute(file_handle.read())
62+
63+
64+
def mathesar_connection(*args, **kwargs):
65+
"""
66+
All Mathesar psycopg connections should use this function. It adds an
67+
application_name to each connection (prepending any previously-set
68+
application name). This is visible in the `pg_stat_activity` table,
69+
useful for debugging (and perhaps required by some DBAs).
70+
"""
71+
kwargs.update(application_name="Mathesar " + kwargs.get("application_name", ""))
72+
return psycopg.connect(*args, **kwargs)

db/deprecated/engine.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ def create_engine(conn_url, *args, **kwargs):
4545
across all engines. This is important for testing: without this intervention, fixtures become
4646
randomly corrupted.
4747
"""
48+
kwargs.update(
49+
connect_args={"application_name": "Mathesar db.deprecated.engine.create_future_engine"},
50+
pool_size=2,
51+
)
4852
engine = sa_create_engine(conn_url, *args, **kwargs)
4953
_make_ischema_names_unique(engine)
5054
return engine

db/deprecated/utils.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import inspect
22
import warnings
33

4-
import psycopg
54
from psycopg2 import errors as p_errors
65
import sqlalchemy
76
from sqlalchemy.exc import ProgrammingError
87

8+
from db.connection import mathesar_connection
9+
910

1011
class UndefinedFunction(Exception):
1112
pass
@@ -74,10 +75,11 @@ def get_pg_catalog_table(table_name, engine, metadata):
7475

7576

7677
def engine_to_psycopg_conn(engine):
77-
return psycopg.connect(
78+
return mathesar_connection(
7879
host=engine.url.host or engine.url.query["host"],
7980
port=engine.url.port,
8081
dbname=engine.url.database,
8182
user=engine.url.username,
82-
password=engine.url.password
83+
password=engine.url.password,
84+
application_name='mathesar.db.deprecated.utils.engine_to_psycopg_conn',
8385
)

0 commit comments

Comments
 (0)