ff-ltitoolkit 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ff_ltitoolkit-0.1.0.dist-info/METADATA +98 -0
- ff_ltitoolkit-0.1.0.dist-info/RECORD +94 -0
- ff_ltitoolkit-0.1.0.dist-info/WHEEL +4 -0
- ff_ltitoolkit-0.1.0.dist-info/licenses/LICENSE +21 -0
- ltitoolkit/__init__.py +20 -0
- ltitoolkit/adapters/__init__.py +11 -0
- ltitoolkit/adapters/brightspace/__init__.py +35 -0
- ltitoolkit/adapters/brightspace/client.py +176 -0
- ltitoolkit/adapters/canvas/__init__.py +27 -0
- ltitoolkit/adapters/canvas/client.py +142 -0
- ltitoolkit/advantage/__init__.py +9 -0
- ltitoolkit/advantage/service.py +96 -0
- ltitoolkit/core/__init__.py +19 -0
- ltitoolkit/core/actions.py +6 -0
- ltitoolkit/core/assignments_grades.py +300 -0
- ltitoolkit/core/contrib/__init__.py +0 -0
- ltitoolkit/core/contrib/django/__init__.py +5 -0
- ltitoolkit/core/contrib/django/cookie.py +56 -0
- ltitoolkit/core/contrib/django/launch_data_storage/__init__.py +0 -0
- ltitoolkit/core/contrib/django/launch_data_storage/cache.py +10 -0
- ltitoolkit/core/contrib/django/lti1p3_tool_config/__init__.py +139 -0
- ltitoolkit/core/contrib/django/lti1p3_tool_config/admin.py +48 -0
- ltitoolkit/core/contrib/django/lti1p3_tool_config/apps.py +6 -0
- ltitoolkit/core/contrib/django/lti1p3_tool_config/migrations/0001_initial.py +168 -0
- ltitoolkit/core/contrib/django/lti1p3_tool_config/migrations/__init__.py +0 -0
- ltitoolkit/core/contrib/django/lti1p3_tool_config/models.py +185 -0
- ltitoolkit/core/contrib/django/message_launch.py +39 -0
- ltitoolkit/core/contrib/django/oidc_login.py +41 -0
- ltitoolkit/core/contrib/django/redirect.py +34 -0
- ltitoolkit/core/contrib/django/request.py +32 -0
- ltitoolkit/core/contrib/django/session.py +5 -0
- ltitoolkit/core/contrib/flask/__init__.py +7 -0
- ltitoolkit/core/contrib/flask/cookie.py +34 -0
- ltitoolkit/core/contrib/flask/launch_data_storage/__init__.py +0 -0
- ltitoolkit/core/contrib/flask/launch_data_storage/cache.py +9 -0
- ltitoolkit/core/contrib/flask/message_launch.py +32 -0
- ltitoolkit/core/contrib/flask/oidc_login.py +31 -0
- ltitoolkit/core/contrib/flask/redirect.py +34 -0
- ltitoolkit/core/contrib/flask/request.py +40 -0
- ltitoolkit/core/contrib/flask/session.py +5 -0
- ltitoolkit/core/contrib/py.typed +0 -0
- ltitoolkit/core/cookie.py +17 -0
- ltitoolkit/core/cookies_allowed_check.py +151 -0
- ltitoolkit/core/course_groups.py +115 -0
- ltitoolkit/core/deep_link.py +100 -0
- ltitoolkit/core/deep_link_resource.py +96 -0
- ltitoolkit/core/deployment.py +13 -0
- ltitoolkit/core/exception.py +16 -0
- ltitoolkit/core/grade.py +143 -0
- ltitoolkit/core/launch_data_storage/__init__.py +0 -0
- ltitoolkit/core/launch_data_storage/base.py +75 -0
- ltitoolkit/core/launch_data_storage/cache.py +43 -0
- ltitoolkit/core/launch_data_storage/session.py +29 -0
- ltitoolkit/core/lineitem.py +205 -0
- ltitoolkit/core/message_launch.py +828 -0
- ltitoolkit/core/message_validators/__init__.py +13 -0
- ltitoolkit/core/message_validators/abstract.py +25 -0
- ltitoolkit/core/message_validators/deep_link.py +34 -0
- ltitoolkit/core/message_validators/privacy_launch.py +40 -0
- ltitoolkit/core/message_validators/resource_message.py +21 -0
- ltitoolkit/core/message_validators/submission_review.py +45 -0
- ltitoolkit/core/names_roles.py +97 -0
- ltitoolkit/core/oidc_login.py +275 -0
- ltitoolkit/core/py.typed +0 -0
- ltitoolkit/core/redirect.py +24 -0
- ltitoolkit/core/registration.py +119 -0
- ltitoolkit/core/request.py +17 -0
- ltitoolkit/core/roles.py +109 -0
- ltitoolkit/core/service_connector.py +144 -0
- ltitoolkit/core/session.py +70 -0
- ltitoolkit/core/tool_config/__init__.py +4 -0
- ltitoolkit/core/tool_config/abstract.py +117 -0
- ltitoolkit/core/tool_config/dict.py +253 -0
- ltitoolkit/core/tool_config/json_file.py +100 -0
- ltitoolkit/core/tool_config/py.typed +0 -0
- ltitoolkit/core/utils.py +10 -0
- ltitoolkit/dynamic_registration/__init__.py +39 -0
- ltitoolkit/dynamic_registration/models.py +192 -0
- ltitoolkit/dynamic_registration/service.py +156 -0
- ltitoolkit/dynamic_registration/store.py +40 -0
- ltitoolkit/dynamic_registration/tool_conf.py +102 -0
- ltitoolkit/exceptions.py +42 -0
- ltitoolkit/fastapi/__init__.py +30 -0
- ltitoolkit/fastapi/cookie.py +53 -0
- ltitoolkit/fastapi/dynamic_registration.py +40 -0
- ltitoolkit/fastapi/message_launch.py +60 -0
- ltitoolkit/fastapi/oidc_login.py +47 -0
- ltitoolkit/fastapi/redirect.py +54 -0
- ltitoolkit/fastapi/request.py +77 -0
- ltitoolkit/fastapi/session.py +13 -0
- ltitoolkit/http.py +80 -0
- ltitoolkit/token/__init__.py +20 -0
- ltitoolkit/token/cache.py +47 -0
- ltitoolkit/token/service.py +165 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# mypy: ignore-errors
|
|
2
|
+
import django.core.validators
|
|
3
|
+
import django.db.models.deletion
|
|
4
|
+
from django.db import migrations, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
initial = True
|
|
10
|
+
|
|
11
|
+
dependencies = []
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.CreateModel(
|
|
15
|
+
name="LtiToolKey",
|
|
16
|
+
fields=[
|
|
17
|
+
(
|
|
18
|
+
"id",
|
|
19
|
+
models.AutoField(
|
|
20
|
+
auto_created=True,
|
|
21
|
+
primary_key=True,
|
|
22
|
+
serialize=False,
|
|
23
|
+
verbose_name="ID",
|
|
24
|
+
),
|
|
25
|
+
),
|
|
26
|
+
(
|
|
27
|
+
"name",
|
|
28
|
+
models.CharField(help_text="Key name", max_length=255, unique=True),
|
|
29
|
+
),
|
|
30
|
+
(
|
|
31
|
+
"private_key",
|
|
32
|
+
models.TextField(
|
|
33
|
+
help_text="Tool's generated Private key. "
|
|
34
|
+
"Keep this value in secret"
|
|
35
|
+
),
|
|
36
|
+
),
|
|
37
|
+
(
|
|
38
|
+
"public_key",
|
|
39
|
+
models.TextField(
|
|
40
|
+
blank=True, help_text="Tool's generated Public key", null=True
|
|
41
|
+
),
|
|
42
|
+
),
|
|
43
|
+
(
|
|
44
|
+
"public_jwk",
|
|
45
|
+
models.TextField(
|
|
46
|
+
blank=True,
|
|
47
|
+
help_text="Tool's generated Public key (from the "
|
|
48
|
+
"field above) presented as JWK.",
|
|
49
|
+
null=True,
|
|
50
|
+
),
|
|
51
|
+
),
|
|
52
|
+
],
|
|
53
|
+
options={
|
|
54
|
+
"verbose_name": "lti 1.3 tool key",
|
|
55
|
+
"verbose_name_plural": "lti 1.3 tool keys",
|
|
56
|
+
"db_table": "lti1p3_tool_key",
|
|
57
|
+
},
|
|
58
|
+
),
|
|
59
|
+
migrations.CreateModel(
|
|
60
|
+
name="LtiTool",
|
|
61
|
+
fields=[
|
|
62
|
+
(
|
|
63
|
+
"id",
|
|
64
|
+
models.AutoField(
|
|
65
|
+
auto_created=True,
|
|
66
|
+
primary_key=True,
|
|
67
|
+
serialize=False,
|
|
68
|
+
verbose_name="ID",
|
|
69
|
+
),
|
|
70
|
+
),
|
|
71
|
+
("title", models.CharField(max_length=255)),
|
|
72
|
+
("is_active", models.BooleanField(default=True)),
|
|
73
|
+
(
|
|
74
|
+
"issuer",
|
|
75
|
+
models.CharField(
|
|
76
|
+
help_text="This will usually look something like 'http://example.com'. "
|
|
77
|
+
"Value provided by LTI 1.3 Platform",
|
|
78
|
+
max_length=255,
|
|
79
|
+
),
|
|
80
|
+
),
|
|
81
|
+
(
|
|
82
|
+
"client_id",
|
|
83
|
+
models.CharField(
|
|
84
|
+
help_text="Value provided by LTI 1.3 Platform", max_length=255
|
|
85
|
+
),
|
|
86
|
+
),
|
|
87
|
+
(
|
|
88
|
+
"use_by_default",
|
|
89
|
+
models.BooleanField(
|
|
90
|
+
default=False,
|
|
91
|
+
help_text="This iss config will be used in case "
|
|
92
|
+
"if client-id was not passed",
|
|
93
|
+
),
|
|
94
|
+
),
|
|
95
|
+
(
|
|
96
|
+
"auth_login_url",
|
|
97
|
+
models.CharField(
|
|
98
|
+
help_text="The platform's OIDC login endpoint. "
|
|
99
|
+
"Value provided by LTI 1.3 Platform",
|
|
100
|
+
max_length=1024,
|
|
101
|
+
validators=[django.core.validators.URLValidator()],
|
|
102
|
+
),
|
|
103
|
+
),
|
|
104
|
+
(
|
|
105
|
+
"auth_token_url",
|
|
106
|
+
models.CharField(
|
|
107
|
+
help_text="The platform's service authorization endpoint. "
|
|
108
|
+
"Value provided by LTI 1.3 Platform",
|
|
109
|
+
max_length=1024,
|
|
110
|
+
validators=[django.core.validators.URLValidator()],
|
|
111
|
+
),
|
|
112
|
+
),
|
|
113
|
+
(
|
|
114
|
+
"auth_audience",
|
|
115
|
+
models.CharField(
|
|
116
|
+
blank=True,
|
|
117
|
+
help_text="The platform's OAuth2 Audience (aud). "
|
|
118
|
+
"Usually could be skipped",
|
|
119
|
+
max_length=1024,
|
|
120
|
+
null=True,
|
|
121
|
+
),
|
|
122
|
+
),
|
|
123
|
+
(
|
|
124
|
+
"key_set_url",
|
|
125
|
+
models.CharField(
|
|
126
|
+
blank=True,
|
|
127
|
+
help_text="The platform's JWKS endpoint. "
|
|
128
|
+
"Value provided by LTI 1.3 Platform",
|
|
129
|
+
max_length=1024,
|
|
130
|
+
null=True,
|
|
131
|
+
validators=[django.core.validators.URLValidator()],
|
|
132
|
+
),
|
|
133
|
+
),
|
|
134
|
+
(
|
|
135
|
+
"key_set",
|
|
136
|
+
models.TextField(
|
|
137
|
+
blank=True,
|
|
138
|
+
help_text="In case if platform's JWKS endpoint somehow "
|
|
139
|
+
"unavailable you may paste JWKS here. Value "
|
|
140
|
+
"provided by LTI 1.3 Platform",
|
|
141
|
+
null=True,
|
|
142
|
+
),
|
|
143
|
+
),
|
|
144
|
+
(
|
|
145
|
+
"deployment_ids",
|
|
146
|
+
models.TextField(
|
|
147
|
+
help_text="List of Deployment IDs. Example: "
|
|
148
|
+
'["test-id-1", "test-id-2", ...] Each value '
|
|
149
|
+
"is provided by LTI 1.3 Platform. "
|
|
150
|
+
),
|
|
151
|
+
),
|
|
152
|
+
(
|
|
153
|
+
"tool_key",
|
|
154
|
+
models.ForeignKey(
|
|
155
|
+
on_delete=django.db.models.deletion.PROTECT,
|
|
156
|
+
related_name="lti_tools",
|
|
157
|
+
to="lti1p3_tool_config.LtiToolKey",
|
|
158
|
+
),
|
|
159
|
+
),
|
|
160
|
+
],
|
|
161
|
+
options={
|
|
162
|
+
"verbose_name": "lti 1.3 tool",
|
|
163
|
+
"verbose_name_plural": "lti 1.3 tools",
|
|
164
|
+
"db_table": "lti1p3_tool",
|
|
165
|
+
"unique_together": {("issuer", "client_id")},
|
|
166
|
+
},
|
|
167
|
+
),
|
|
168
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# mypy: ignore-errors
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
from django.core.exceptions import ValidationError
|
|
5
|
+
from django.core.validators import URLValidator
|
|
6
|
+
from django.db import models
|
|
7
|
+
from django.utils.translation import gettext_lazy as _
|
|
8
|
+
from ltitoolkit.core.registration import Registration
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LtiToolKey(models.Model):
|
|
12
|
+
name = models.CharField(
|
|
13
|
+
max_length=255, null=False, blank=False, unique=True, help_text=_("Key name")
|
|
14
|
+
)
|
|
15
|
+
private_key = models.TextField(
|
|
16
|
+
null=False,
|
|
17
|
+
blank=False,
|
|
18
|
+
help_text=_("Tool's generated Private key. Keep this value in secret"),
|
|
19
|
+
)
|
|
20
|
+
public_key = models.TextField(
|
|
21
|
+
null=True, blank=True, help_text=_("Tool's generated Public key")
|
|
22
|
+
)
|
|
23
|
+
public_jwk = models.TextField(
|
|
24
|
+
null=True,
|
|
25
|
+
blank=True,
|
|
26
|
+
help_text=_(
|
|
27
|
+
"Tool's generated Public key (from the field above) presented as JWK."
|
|
28
|
+
),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
def save(
|
|
32
|
+
self, *args, **kwargs
|
|
33
|
+
): # pylint: disable=arguments-differ,signature-differs
|
|
34
|
+
if self.public_key:
|
|
35
|
+
public_jwk_dict = Registration.get_jwk(self.public_key)
|
|
36
|
+
self.public_jwk = json.dumps(public_jwk_dict)
|
|
37
|
+
else:
|
|
38
|
+
self.public_key = None
|
|
39
|
+
self.public_jwk = None
|
|
40
|
+
super().save(*args, **kwargs)
|
|
41
|
+
|
|
42
|
+
def __str__(self):
|
|
43
|
+
# pylint: disable=no-member
|
|
44
|
+
return f"<LtiToolKey id={self.id}, name={self.name}>"
|
|
45
|
+
|
|
46
|
+
class Meta:
|
|
47
|
+
db_table = "lti1p3_tool_key"
|
|
48
|
+
verbose_name = "lti 1.3 tool key"
|
|
49
|
+
verbose_name_plural = "lti 1.3 tool keys"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class LtiTool(models.Model):
|
|
53
|
+
title = models.CharField(max_length=255)
|
|
54
|
+
is_active = models.BooleanField(default=True)
|
|
55
|
+
issuer = models.CharField(
|
|
56
|
+
max_length=255,
|
|
57
|
+
help_text=_(
|
|
58
|
+
"This will usually look something like 'http://example.com'. "
|
|
59
|
+
"Value provided by LTI 1.3 Platform"
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
client_id = models.CharField(
|
|
63
|
+
max_length=255,
|
|
64
|
+
null=False,
|
|
65
|
+
blank=False,
|
|
66
|
+
help_text=_("Value provided by LTI 1.3 Platform"),
|
|
67
|
+
)
|
|
68
|
+
use_by_default = models.BooleanField(
|
|
69
|
+
default=False,
|
|
70
|
+
help_text=_("This iss config will be used in case if client-id was not passed"),
|
|
71
|
+
)
|
|
72
|
+
auth_login_url = models.CharField(
|
|
73
|
+
max_length=1024,
|
|
74
|
+
null=False,
|
|
75
|
+
blank=False,
|
|
76
|
+
help_text=_(
|
|
77
|
+
"The platform's OIDC login endpoint. Value provided by LTI 1.3 Platform"
|
|
78
|
+
),
|
|
79
|
+
validators=[URLValidator()],
|
|
80
|
+
)
|
|
81
|
+
auth_token_url = models.CharField(
|
|
82
|
+
max_length=1024,
|
|
83
|
+
null=False,
|
|
84
|
+
blank=False,
|
|
85
|
+
help_text=_(
|
|
86
|
+
"The platform's service authorization "
|
|
87
|
+
"endpoint. Value provided by "
|
|
88
|
+
"LTI 1.3 Platform"
|
|
89
|
+
),
|
|
90
|
+
validators=[URLValidator()],
|
|
91
|
+
)
|
|
92
|
+
auth_audience = models.CharField(
|
|
93
|
+
max_length=1024,
|
|
94
|
+
null=True,
|
|
95
|
+
blank=True,
|
|
96
|
+
help_text=_("The platform's OAuth2 Audience (aud). Usually could be skipped"),
|
|
97
|
+
)
|
|
98
|
+
key_set_url = models.CharField(
|
|
99
|
+
max_length=1024,
|
|
100
|
+
null=True,
|
|
101
|
+
blank=True,
|
|
102
|
+
help_text=_("The platform's JWKS endpoint. Value provided by LTI 1.3 Platform"),
|
|
103
|
+
validators=[URLValidator()],
|
|
104
|
+
)
|
|
105
|
+
key_set = models.TextField(
|
|
106
|
+
null=True,
|
|
107
|
+
blank=True,
|
|
108
|
+
help_text=_(
|
|
109
|
+
"In case if platform's JWKS endpoint somehow "
|
|
110
|
+
"unavailable you may paste JWKS here. "
|
|
111
|
+
"Value provided by LTI 1.3 Platform"
|
|
112
|
+
),
|
|
113
|
+
)
|
|
114
|
+
tool_key = models.ForeignKey(
|
|
115
|
+
LtiToolKey, on_delete=models.PROTECT, related_name="lti_tools"
|
|
116
|
+
)
|
|
117
|
+
deployment_ids = models.TextField(
|
|
118
|
+
null=False,
|
|
119
|
+
blank=False,
|
|
120
|
+
help_text=_(
|
|
121
|
+
"List of Deployment IDs. "
|
|
122
|
+
'Example: ["test-id-1", "test-id-2", ...] '
|
|
123
|
+
"Each value is provided by LTI 1.3 Platform. "
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
def clean(self):
|
|
128
|
+
if not self.key_set_url and not self.key_set:
|
|
129
|
+
raise ValidationError(
|
|
130
|
+
{
|
|
131
|
+
"key_set_url": _(
|
|
132
|
+
'Even one of "key_set_url" or "key_set" should be set'
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
if self.key_set:
|
|
138
|
+
key_set_valid = False
|
|
139
|
+
try:
|
|
140
|
+
key_set_data = json.loads(self.key_set)
|
|
141
|
+
if isinstance(key_set_data, dict):
|
|
142
|
+
key_set_valid = True
|
|
143
|
+
except ValueError:
|
|
144
|
+
pass
|
|
145
|
+
if not key_set_valid:
|
|
146
|
+
raise ValidationError({"key_set": _("Should be a dict")})
|
|
147
|
+
|
|
148
|
+
deployment_ids_valid = False
|
|
149
|
+
try:
|
|
150
|
+
deployment_ids_data = json.loads(self.deployment_ids)
|
|
151
|
+
if isinstance(deployment_ids_data, list):
|
|
152
|
+
deployment_ids_valid = True
|
|
153
|
+
except ValueError:
|
|
154
|
+
pass
|
|
155
|
+
if not deployment_ids_valid:
|
|
156
|
+
raise ValidationError(
|
|
157
|
+
{
|
|
158
|
+
"deployment_ids": _(
|
|
159
|
+
'Should be a list. Example: ["test-id-1", "test-id-2", ...]'
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
def to_dict(self):
|
|
165
|
+
data = {
|
|
166
|
+
"issuer": self.issuer,
|
|
167
|
+
"client_id": self.client_id,
|
|
168
|
+
"auth_login_url": self.auth_login_url,
|
|
169
|
+
"auth_token_url": self.auth_token_url,
|
|
170
|
+
"auth_audience": self.auth_audience,
|
|
171
|
+
"key_set_url": self.key_set_url,
|
|
172
|
+
"key_set": json.loads(self.key_set) if self.key_set else None,
|
|
173
|
+
"deployment_ids": json.loads(self.deployment_ids)
|
|
174
|
+
if self.deployment_ids
|
|
175
|
+
else [],
|
|
176
|
+
}
|
|
177
|
+
return data
|
|
178
|
+
|
|
179
|
+
class Meta:
|
|
180
|
+
unique_together = [
|
|
181
|
+
["issuer", "client_id"],
|
|
182
|
+
]
|
|
183
|
+
db_table = "lti1p3_tool"
|
|
184
|
+
verbose_name = "lti 1.3 tool"
|
|
185
|
+
verbose_name_plural = "lti 1.3 tools"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from ltitoolkit.core.message_launch import MessageLaunch
|
|
2
|
+
from ltitoolkit.core.request import Request
|
|
3
|
+
from .cookie import DjangoCookieService
|
|
4
|
+
from .request import DjangoRequest
|
|
5
|
+
from .session import DjangoSessionService
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DjangoMessageLaunch(MessageLaunch):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
request,
|
|
12
|
+
tool_config,
|
|
13
|
+
session_service=None,
|
|
14
|
+
cookie_service=None,
|
|
15
|
+
launch_data_storage=None,
|
|
16
|
+
requests_session=None,
|
|
17
|
+
):
|
|
18
|
+
django_request = (
|
|
19
|
+
request
|
|
20
|
+
if isinstance(request, Request)
|
|
21
|
+
else DjangoRequest(request, post_only=True)
|
|
22
|
+
)
|
|
23
|
+
cookie_service = (
|
|
24
|
+
cookie_service if cookie_service else DjangoCookieService(django_request)
|
|
25
|
+
)
|
|
26
|
+
session_service = (
|
|
27
|
+
session_service if session_service else DjangoSessionService(request)
|
|
28
|
+
)
|
|
29
|
+
super().__init__(
|
|
30
|
+
django_request,
|
|
31
|
+
tool_config,
|
|
32
|
+
session_service,
|
|
33
|
+
cookie_service,
|
|
34
|
+
launch_data_storage,
|
|
35
|
+
requests_session,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def _get_request_param(self, key):
|
|
39
|
+
return self._request.get_param(key)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from django.http import HttpResponse # type: ignore
|
|
2
|
+
from ltitoolkit.core.oidc_login import OIDCLogin
|
|
3
|
+
from ltitoolkit.core.request import Request
|
|
4
|
+
|
|
5
|
+
from .cookie import DjangoCookieService
|
|
6
|
+
from .redirect import DjangoRedirect
|
|
7
|
+
from .request import DjangoRequest
|
|
8
|
+
from .session import DjangoSessionService
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DjangoOIDCLogin(OIDCLogin):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
request,
|
|
15
|
+
tool_config,
|
|
16
|
+
session_service=None,
|
|
17
|
+
cookie_service=None,
|
|
18
|
+
launch_data_storage=None,
|
|
19
|
+
):
|
|
20
|
+
django_request = (
|
|
21
|
+
request if isinstance(request, Request) else DjangoRequest(request)
|
|
22
|
+
)
|
|
23
|
+
cookie_service = (
|
|
24
|
+
cookie_service if cookie_service else DjangoCookieService(django_request)
|
|
25
|
+
)
|
|
26
|
+
session_service = (
|
|
27
|
+
session_service if session_service else DjangoSessionService(request)
|
|
28
|
+
)
|
|
29
|
+
super().__init__(
|
|
30
|
+
django_request,
|
|
31
|
+
tool_config,
|
|
32
|
+
session_service,
|
|
33
|
+
cookie_service,
|
|
34
|
+
launch_data_storage,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def get_redirect(self, url):
|
|
38
|
+
return DjangoRedirect(url, self._cookie_service)
|
|
39
|
+
|
|
40
|
+
def get_response(self, html):
|
|
41
|
+
return HttpResponse(html)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from django.http import HttpResponse # type: ignore
|
|
2
|
+
from django.shortcuts import redirect # type: ignore
|
|
3
|
+
from ltitoolkit.core.redirect import Redirect
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DjangoRedirect(Redirect):
|
|
7
|
+
_location = None
|
|
8
|
+
_cookie_service = None
|
|
9
|
+
|
|
10
|
+
def __init__(self, location, cookie_service=None):
|
|
11
|
+
super().__init__()
|
|
12
|
+
self._location = location
|
|
13
|
+
self._cookie_service = cookie_service
|
|
14
|
+
|
|
15
|
+
def do_redirect(self):
|
|
16
|
+
return self._process_response(redirect(self._location))
|
|
17
|
+
|
|
18
|
+
def do_js_redirect(self):
|
|
19
|
+
return self._process_response(
|
|
20
|
+
HttpResponse(
|
|
21
|
+
f'<script type="text/javascript">window.location="{self._location}";</script>'
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
def set_redirect_url(self, location):
|
|
26
|
+
self._location = location
|
|
27
|
+
|
|
28
|
+
def get_redirect_url(self):
|
|
29
|
+
return self._location
|
|
30
|
+
|
|
31
|
+
def _process_response(self, response):
|
|
32
|
+
if self._cookie_service:
|
|
33
|
+
self._cookie_service.update_response(response)
|
|
34
|
+
return response
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from ltitoolkit.core.request import Request
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DjangoRequest(Request):
|
|
5
|
+
_request = None
|
|
6
|
+
_post_only = False
|
|
7
|
+
_default_params = None
|
|
8
|
+
|
|
9
|
+
@property
|
|
10
|
+
def session(self):
|
|
11
|
+
return self._request.session
|
|
12
|
+
|
|
13
|
+
def __init__(self, request, post_only=False, default_params=None):
|
|
14
|
+
self.set_request(request)
|
|
15
|
+
self._post_only = post_only
|
|
16
|
+
self._default_params = default_params if default_params else {}
|
|
17
|
+
|
|
18
|
+
def set_request(self, request):
|
|
19
|
+
self._request = request
|
|
20
|
+
|
|
21
|
+
def get_param(self, key):
|
|
22
|
+
if self._post_only:
|
|
23
|
+
return self._request.POST.get(key, self._default_params.get(key))
|
|
24
|
+
return self._request.GET.get(
|
|
25
|
+
key, self._request.POST.get(key, self._default_params.get(key))
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def get_cookie(self, key):
|
|
29
|
+
return self._request.COOKIES.get(key)
|
|
30
|
+
|
|
31
|
+
def is_secure(self):
|
|
32
|
+
return self._request.is_secure()
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# flake8: noqa
|
|
2
|
+
from .cookie import FlaskCookieService
|
|
3
|
+
from .oidc_login import FlaskOIDCLogin
|
|
4
|
+
from .message_launch import FlaskMessageLaunch
|
|
5
|
+
from .request import FlaskRequest
|
|
6
|
+
from .session import FlaskSessionService
|
|
7
|
+
from .launch_data_storage.cache import FlaskCacheDataStorage
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from ltitoolkit.core.cookie import CookieService
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class FlaskCookieService(CookieService):
|
|
5
|
+
_request = None
|
|
6
|
+
_cookie_data_to_set = None
|
|
7
|
+
|
|
8
|
+
def __init__(self, request):
|
|
9
|
+
self._request = request
|
|
10
|
+
self._cookie_data_to_set = {}
|
|
11
|
+
|
|
12
|
+
def _get_key(self, key):
|
|
13
|
+
return self._cookie_prefix + "-" + key
|
|
14
|
+
|
|
15
|
+
def get_cookie(self, name):
|
|
16
|
+
return self._request.get_cookie(self._get_key(name))
|
|
17
|
+
|
|
18
|
+
def set_cookie(self, name, value, exp=3600):
|
|
19
|
+
self._cookie_data_to_set[self._get_key(name)] = {"value": value, "exp": exp}
|
|
20
|
+
|
|
21
|
+
def update_response(self, response):
|
|
22
|
+
for key, cookie_data in self._cookie_data_to_set.items():
|
|
23
|
+
cookie_kwargs = dict(
|
|
24
|
+
key=key,
|
|
25
|
+
value=cookie_data["value"],
|
|
26
|
+
max_age=cookie_data["exp"],
|
|
27
|
+
secure=self._request.is_secure(),
|
|
28
|
+
path="/",
|
|
29
|
+
httponly=True,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
if self._request.is_secure():
|
|
33
|
+
cookie_kwargs["samesite"] = "None"
|
|
34
|
+
response.set_cookie(**cookie_kwargs)
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from ltitoolkit.core.message_launch import MessageLaunch
|
|
2
|
+
from .cookie import FlaskCookieService
|
|
3
|
+
from .session import FlaskSessionService
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FlaskMessageLaunch(MessageLaunch):
|
|
7
|
+
def __init__(
|
|
8
|
+
self,
|
|
9
|
+
request,
|
|
10
|
+
tool_config,
|
|
11
|
+
session_service=None,
|
|
12
|
+
cookie_service=None,
|
|
13
|
+
launch_data_storage=None,
|
|
14
|
+
requests_session=None,
|
|
15
|
+
):
|
|
16
|
+
cookie_service = (
|
|
17
|
+
cookie_service if cookie_service else FlaskCookieService(request)
|
|
18
|
+
)
|
|
19
|
+
session_service = (
|
|
20
|
+
session_service if session_service else FlaskSessionService(request)
|
|
21
|
+
)
|
|
22
|
+
super().__init__(
|
|
23
|
+
request,
|
|
24
|
+
tool_config,
|
|
25
|
+
session_service,
|
|
26
|
+
cookie_service,
|
|
27
|
+
launch_data_storage,
|
|
28
|
+
requests_session,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
def _get_request_param(self, key):
|
|
32
|
+
return self._request.get_param(key)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from flask import make_response # type: ignore
|
|
2
|
+
from ltitoolkit.core.oidc_login import OIDCLogin
|
|
3
|
+
from .cookie import FlaskCookieService
|
|
4
|
+
from .session import FlaskSessionService
|
|
5
|
+
from .redirect import FlaskRedirect
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FlaskOIDCLogin(OIDCLogin):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
request,
|
|
12
|
+
tool_config,
|
|
13
|
+
session_service=None,
|
|
14
|
+
cookie_service=None,
|
|
15
|
+
launch_data_storage=None,
|
|
16
|
+
):
|
|
17
|
+
cookie_service = (
|
|
18
|
+
cookie_service if cookie_service else FlaskCookieService(request)
|
|
19
|
+
)
|
|
20
|
+
session_service = (
|
|
21
|
+
session_service if session_service else FlaskSessionService(request)
|
|
22
|
+
)
|
|
23
|
+
super().__init__(
|
|
24
|
+
request, tool_config, session_service, cookie_service, launch_data_storage
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def get_redirect(self, url):
|
|
28
|
+
return FlaskRedirect(url, self._cookie_service)
|
|
29
|
+
|
|
30
|
+
def get_response(self, html):
|
|
31
|
+
return make_response(html)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from flask import make_response, redirect # type: ignore
|
|
2
|
+
|
|
3
|
+
from ltitoolkit.core.redirect import Redirect
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FlaskRedirect(Redirect):
|
|
7
|
+
_location = None
|
|
8
|
+
_cookie_service = None
|
|
9
|
+
|
|
10
|
+
def __init__(self, location, cookie_service=None):
|
|
11
|
+
super().__init__()
|
|
12
|
+
self._location = location
|
|
13
|
+
self._cookie_service = cookie_service
|
|
14
|
+
|
|
15
|
+
def do_redirect(self):
|
|
16
|
+
return self._process_response(redirect(self._location))
|
|
17
|
+
|
|
18
|
+
def do_js_redirect(self):
|
|
19
|
+
return self._process_response(
|
|
20
|
+
make_response(
|
|
21
|
+
f'<script type="text/javascript">window.location="{self._location}";</script>'
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
def set_redirect_url(self, location):
|
|
26
|
+
self._location = location
|
|
27
|
+
|
|
28
|
+
def get_redirect_url(self):
|
|
29
|
+
return self._location
|
|
30
|
+
|
|
31
|
+
def _process_response(self, response):
|
|
32
|
+
if self._cookie_service:
|
|
33
|
+
self._cookie_service.update_response(response)
|
|
34
|
+
return response
|