c2cgeoportal-commons 2.6.0__py3-none-any.whl → 2.8.1.180__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.

Potentially problematic release.


This version of c2cgeoportal-commons might be problematic. Click here for more details.

Files changed (82) hide show
  1. c2cgeoportal_commons/__init__.py +2 -5
  2. c2cgeoportal_commons/alembic/env.py +40 -28
  3. c2cgeoportal_commons/alembic/main/028477929d13_add_technical_roles.py +10 -7
  4. c2cgeoportal_commons/alembic/main/04f05bfbb05e_remove_the_old_is_expanded_column.py +60 -0
  5. c2cgeoportal_commons/alembic/main/116b9b79fc4d_internal_and_external_layer_tables_.py +42 -43
  6. c2cgeoportal_commons/alembic/main/1418cb05921b_merge_1_6_and_master_branches.py +7 -8
  7. c2cgeoportal_commons/alembic/main/164ac0819a61_add_image_format_to_wmts_layer.py +9 -6
  8. c2cgeoportal_commons/alembic/main/166ff2dcc48d_create_database.py +21 -24
  9. c2cgeoportal_commons/alembic/main/16e43f8c0330_remove_old_metadata_column.py +9 -6
  10. c2cgeoportal_commons/alembic/main/1d5d4abfebd1_add_restricted_theme.py +14 -8
  11. c2cgeoportal_commons/alembic/main/1de20166b274_remove_v1_artifacts.py +9 -6
  12. c2cgeoportal_commons/alembic/main/20137477bd02_update_icons_url.py +9 -6
  13. c2cgeoportal_commons/alembic/main/21f11066f8ec_trigger_on_role_updates_user_in_static.py +21 -20
  14. c2cgeoportal_commons/alembic/main/22e6dfb556de_add_description_tree_.py +9 -6
  15. c2cgeoportal_commons/alembic/main/29f2a32859ec_merge_1_6_and_master_branches.py +7 -8
  16. c2cgeoportal_commons/alembic/main/2b8ed8c1df94_set_layergroup_treeitem_is_as_a_primary_.py +9 -6
  17. c2cgeoportal_commons/alembic/main/32527659d57b_move_exclude_properties_from_layerv1_to_.py +15 -12
  18. c2cgeoportal_commons/alembic/main/32b21aa1d0ed_merge_e004f76e951a_and_e004f76e951a_.py +7 -8
  19. c2cgeoportal_commons/alembic/main/338b57593823_remove_trigger_on_role_name_change.py +25 -24
  20. c2cgeoportal_commons/alembic/main/415746eb9f6_changes_for_v2.py +45 -43
  21. c2cgeoportal_commons/alembic/main/44c91d82d419_add_table_log.py +70 -0
  22. c2cgeoportal_commons/alembic/main/5109242131ce_add_column_time_widget.py +10 -9
  23. c2cgeoportal_commons/alembic/main/52916d8fde8b_add_sql_fields_to_vector_tiles.py +58 -0
  24. c2cgeoportal_commons/alembic/main/53ba1a68d5fe_add_theme_to_fulltextsearch.py +9 -6
  25. c2cgeoportal_commons/alembic/main/54645a535ad6_add_ordering_in_relation.py +17 -14
  26. c2cgeoportal_commons/alembic/main/56dc90838d90_fix_removing_layerv1.py +10 -8
  27. c2cgeoportal_commons/alembic/main/596ba21e3833_separate_local_internal.py +13 -14
  28. c2cgeoportal_commons/alembic/main/678f88c7ad5e_add_vector_tiles_layers_table.py +9 -6
  29. c2cgeoportal_commons/alembic/main/6a412d9437b1_rename_serverogc_to_ogcserver.py +9 -6
  30. c2cgeoportal_commons/alembic/main/6d87fdad275a_convert_metadata_to_the_right_case.py +13 -20
  31. c2cgeoportal_commons/alembic/main/7530011a66a7_trigger_on_role_updates_user_in_static.py +21 -20
  32. c2cgeoportal_commons/alembic/main/78fd093c8393_add_api_s_intrfaces.py +27 -48
  33. c2cgeoportal_commons/alembic/main/7d271f4527cd_add_layer_column_in_layerv1_table.py +9 -6
  34. c2cgeoportal_commons/alembic/main/809650bd04c3_add_dimension_field.py +9 -6
  35. c2cgeoportal_commons/alembic/main/8117bb9bba16_use_dimension_on_all_the_layers.py +9 -6
  36. c2cgeoportal_commons/alembic/main/87f8330ed64e_add_missing_delete_cascades.py +9 -6
  37. c2cgeoportal_commons/alembic/main/9268a1dffac0_add_trigger_to_be_able_to_correctly_.py +11 -8
  38. c2cgeoportal_commons/alembic/main/94db7e7e5b21_merge_2_2_and_master_branches.py +7 -8
  39. c2cgeoportal_commons/alembic/main/951ff84bd8ec_be_able_to_delete_a_wms_layer_in_sql.py +9 -6
  40. c2cgeoportal_commons/alembic/main/a00109812f89_add_field_layer_wms_valid.py +9 -6
  41. c2cgeoportal_commons/alembic/main/a4f1aac9bda_merge_1_6_and_master_branches.py +7 -8
  42. c2cgeoportal_commons/alembic/main/b60f2a505f42_remame_uimetadata_to_metadata.py +9 -6
  43. c2cgeoportal_commons/alembic/main/c75124553bf3_remove_deprecated_columns.py +9 -6
  44. c2cgeoportal_commons/alembic/main/d48a63b348f1_change_mapserver_url_for_docker.py +13 -14
  45. c2cgeoportal_commons/alembic/main/d8ef99bc227e_be_able_to_delete_a_linked_functionality.py +15 -12
  46. c2cgeoportal_commons/alembic/main/daf738d5bae4_merge_2_0_and_master_branches.py +7 -8
  47. c2cgeoportal_commons/alembic/main/dba87f2647f9_merge_2_2_on_2_3.py +7 -8
  48. c2cgeoportal_commons/alembic/main/e004f76e951a_add_missing_not_null.py +15 -32
  49. c2cgeoportal_commons/alembic/main/e7e03dedade3_put_the_default_wms_server_in_the_.py +13 -14
  50. c2cgeoportal_commons/alembic/main/e85afd327ab3_cascade_deletes_to_tsearch.py +9 -6
  51. c2cgeoportal_commons/alembic/main/ec82a8906649_add_missing_on_delete_cascade_on_layer_.py +13 -10
  52. c2cgeoportal_commons/alembic/main/ee25d267bf46_main_interface_desktop.py +11 -14
  53. c2cgeoportal_commons/alembic/main/eeb345672454_merge_2_4_and_master_branches.py +7 -8
  54. c2cgeoportal_commons/alembic/static/0c640a58a09a_add_opt_key_column.py +9 -6
  55. c2cgeoportal_commons/alembic/static/107b81f5b9fe_add_missing_delete_cascades.py +9 -6
  56. c2cgeoportal_commons/alembic/static/1857owc78a07_add_last_login_and_expiration_date.py +7 -6
  57. c2cgeoportal_commons/alembic/static/1da396a88908_move_user_table_to_static_schema.py +18 -19
  58. c2cgeoportal_commons/alembic/static/3f89a7d71a5e_alter_column_url_to_remove_limitation.py +8 -7
  59. c2cgeoportal_commons/alembic/static/44c91d82d419_add_table_log.py +70 -0
  60. c2cgeoportal_commons/alembic/static/53d671b17b20_add_timezone_on_datetime_fields.py +9 -6
  61. c2cgeoportal_commons/alembic/static/5472fbc19f39_add_temp_password_column.py +9 -6
  62. c2cgeoportal_commons/alembic/static/76d72fb3fcb9_add_oauth2_pkce.py +68 -0
  63. c2cgeoportal_commons/alembic/static/7ef947f30f20_add_oauth2_tables.py +7 -6
  64. c2cgeoportal_commons/alembic/static/ae5e88f35669_add_table_user_role.py +15 -18
  65. c2cgeoportal_commons/alembic/static/bd029dbfc11a_fill_tech_data_column.py +10 -8
  66. c2cgeoportal_commons/lib/email_.py +11 -13
  67. c2cgeoportal_commons/lib/literal.py +44 -0
  68. c2cgeoportal_commons/lib/url.py +164 -57
  69. c2cgeoportal_commons/lib/validators.py +2 -4
  70. c2cgeoportal_commons/models/__init__.py +7 -10
  71. c2cgeoportal_commons/models/main.py +968 -127
  72. c2cgeoportal_commons/models/sqlalchemy.py +13 -18
  73. c2cgeoportal_commons/models/static.py +212 -34
  74. c2cgeoportal_commons/py.typed +0 -0
  75. c2cgeoportal_commons/testing/__init__.py +11 -10
  76. c2cgeoportal_commons/testing/initializedb.py +12 -12
  77. {c2cgeoportal_commons-2.6.0.dist-info → c2cgeoportal_commons-2.8.1.180.dist-info}/METADATA +16 -11
  78. c2cgeoportal_commons-2.8.1.180.dist-info/RECORD +83 -0
  79. {c2cgeoportal_commons-2.6.0.dist-info → c2cgeoportal_commons-2.8.1.180.dist-info}/WHEEL +1 -1
  80. tests/conftest.py +0 -2
  81. c2cgeoportal_commons-2.6.0.dist-info/RECORD +0 -76
  82. {c2cgeoportal_commons-2.6.0.dist-info → c2cgeoportal_commons-2.8.1.180.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2012-2021, Camptocamp SA
1
+ # Copyright (c) 2012-2023, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -27,7 +25,7 @@
27
25
  # of the authors and should not be interpreted as representing official policies,
28
26
  # either expressed or implied, of the FreeBSD Project.
29
27
  import json
30
- from typing import Any, Optional, Type
28
+ from typing import Any, Dict, Optional, Type
31
29
 
32
30
  from sqlalchemy.engine import Dialect
33
31
  from sqlalchemy.types import VARCHAR, TypeDecorator, UserDefinedType
@@ -35,37 +33,34 @@ from sqlalchemy.types import VARCHAR, TypeDecorator, UserDefinedType
35
33
 
36
34
  # get from https://docs.sqlalchemy.org/en/latest/orm/extensions/
37
35
  # mutable.html#establishing-mutability-on-scalar-column-values
38
- class JSONEncodedDict(TypeDecorator):
39
- """
40
- Represents an immutable structure as a json-encoded string.
41
- """
36
+ class JSONEncodedDict(TypeDecorator): # type: ignore
37
+ """Represent an immutable structure as a json-encoded string."""
42
38
 
43
39
  impl = VARCHAR
44
40
 
45
- @staticmethod
46
- def process_bind_param(value: Optional[dict], _: Dialect) -> Optional[str]:
41
+ def process_bind_param(self, value: Optional[Dict[str, Any]], _: Dialect) -> Optional[str]:
47
42
  return json.dumps(value) if value is not None else None
48
43
 
49
- @staticmethod
50
- def process_result_value(value: Optional[str], _: Dialect) -> Optional[dict]:
44
+ def process_result_value(self, value: Optional[str], _: Dialect) -> Optional[Dict[str, Any]]:
51
45
  return json.loads(value) if value is not None else None
52
46
 
53
47
  @property
54
- def python_type(self) -> Type:
48
+ def python_type(self) -> Type[Any]:
55
49
  return dict
56
50
 
57
- @staticmethod
58
- def process_literal_param(value: str, dialect: Any) -> str:
51
+ def process_literal_param(self, value: str, dialect: Any) -> str:
59
52
  del dialect
60
53
  return json.dumps(value)
61
54
 
62
55
 
63
- class TsVector(UserDefinedType):
56
+ class TsVector(UserDefinedType): # type: ignore
64
57
  """A custom type for PostgreSQL's tsvector type."""
65
58
 
66
- def get_col_spec(self) -> str: # pylint: disable=no-self-use
59
+ cache_ok = True
60
+
61
+ def get_col_spec(self) -> str:
67
62
  return "TSVECTOR"
68
63
 
69
64
  @property
70
- def python_type(self) -> Type:
65
+ def python_type(self) -> Type[Any]:
71
66
  return dict
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2011-2021, Camptocamp SA
1
+ # Copyright (c) 2011-2023, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -33,7 +31,7 @@ import logging
33
31
  from datetime import datetime
34
32
  from hashlib import sha1
35
33
  from hmac import compare_digest as compare_hash
36
- from typing import Any, List
34
+ from typing import Any, List, Optional
37
35
 
38
36
  import pytz
39
37
  import sqlalchemy.schema
@@ -44,8 +42,9 @@ from sqlalchemy.ext.mutable import MutableDict
44
42
  from sqlalchemy.orm import backref, relationship
45
43
  from sqlalchemy.types import Boolean, DateTime, Integer, String, Unicode
46
44
 
45
+ from c2cgeoportal_commons.lib.literal import Literal
47
46
  from c2cgeoportal_commons.models import Base, _
48
- from c2cgeoportal_commons.models.main import Role
47
+ from c2cgeoportal_commons.models.main import AbstractLog, Role
49
48
 
50
49
  try:
51
50
  from c2cgeoform.ext.deform_ext import RelationSelect2Widget
@@ -55,6 +54,8 @@ except ModuleNotFoundError:
55
54
  drop = None
56
55
 
57
56
  class GenericClass:
57
+ """Generic class."""
58
+
58
59
  def __init__(self, *args: Any, **kwargs: Any):
59
60
  pass
60
61
 
@@ -79,10 +80,48 @@ user_role = Table(
79
80
  )
80
81
 
81
82
 
82
- class User(Base):
83
+ class User(Base): # type: ignore
84
+ """The user table representation."""
85
+
83
86
  __tablename__ = "user"
84
87
  __table_args__ = {"schema": _schema}
85
- __colanderalchemy_config__ = {"title": _("User"), "plural": _("Users")}
88
+ __colanderalchemy_config__ = {
89
+ "title": _("User"),
90
+ "plural": _("Users"),
91
+ "description": Literal(
92
+ _(
93
+ """
94
+ <div class="help-block">
95
+ <p>Each user may have from 1 to n roles, but each user has a default role from
96
+ which are taken some settings. The default role (defined through the
97
+ "Settings from role" selection) has an influence on the role extent and on some
98
+ functionalities regarding their configuration.</p>
99
+
100
+ <p>Role extents for users can only be set in one role, because the application
101
+ is currently not able to check multiple extents for one user, thus it is the
102
+ default role which defines this unique extent.</p>
103
+
104
+ <p>Any functionality specified as <b>single</b> can be defined only once per user.
105
+ Hence, these functionalities have to be defined in the default role.</p>
106
+
107
+ <p>By default, functionalities are not specified as <b>single</b>. Currently, the
108
+ following functionalities are of <b>single</b> type:</p>
109
+
110
+ <ul>
111
+ <li><code>default_basemap</code></li>
112
+ <li><code>default_theme</code></li>
113
+ <li><code>preset_layer_filter</code></li>
114
+ <li><code>open_panel</code></li>
115
+ </ul>
116
+
117
+ <p>Any other functionality (with <b>single</b> not set or set to <code>false</code>) can
118
+ be defined in any role linked to the user.</p>
119
+ <hr>
120
+ </div>
121
+ """
122
+ )
123
+ ),
124
+ }
86
125
  __c2cgeoform_config__ = {"duplicate": True}
87
126
  item_type = Column(
88
127
  "type", String(10), nullable=False, info={"colanderalchemy": {"widget": HiddenWidget()}}
@@ -91,7 +130,15 @@ class User(Base):
91
130
 
92
131
  id = Column(Integer, primary_key=True, info={"colanderalchemy": {"widget": HiddenWidget()}})
93
132
  username = Column(
94
- Unicode, unique=True, nullable=False, info={"colanderalchemy": {"title": _("Username")}}
133
+ Unicode,
134
+ unique=True,
135
+ nullable=False,
136
+ info={
137
+ "colanderalchemy": {
138
+ "title": _("Username"),
139
+ "description": _("Name used for authentication (must be unique)."),
140
+ }
141
+ },
95
142
  )
96
143
  _password = Column("password", Unicode, nullable=False, info={"colanderalchemy": {"exclude": True}})
97
144
  temp_password = Column(
@@ -99,10 +146,27 @@ class User(Base):
99
146
  )
100
147
  tech_data = Column(MutableDict.as_mutable(HSTORE), info={"colanderalchemy": {"exclude": True}})
101
148
  email = Column(
102
- Unicode, nullable=False, info={"colanderalchemy": {"title": _("Email"), "validator": Email()}}
149
+ Unicode,
150
+ nullable=False,
151
+ info={
152
+ "colanderalchemy": {
153
+ "title": _("Email"),
154
+ "description": _(
155
+ "Used to send emails to the user, for example in case of password recovery."
156
+ ),
157
+ "validator": Email(),
158
+ }
159
+ },
103
160
  )
104
161
  is_password_changed = Column(
105
- Boolean, default=False, info={"colanderalchemy": {"title": _("The user changed his password")}}
162
+ Boolean,
163
+ default=False,
164
+ info={
165
+ "colanderalchemy": {
166
+ "title": _("The user changed his password"),
167
+ "description": _("Indicates if user has changed his password."),
168
+ }
169
+ },
106
170
  )
107
171
 
108
172
  settings_role_id = Column(
@@ -110,7 +174,7 @@ class User(Base):
110
174
  info={
111
175
  "colanderalchemy": {
112
176
  "title": _("Settings from role"),
113
- "description": "Only used for settings not for permissions",
177
+ "description": _("Used to get some settings for the user (not for permissions)."),
114
178
  "widget": RelationSelect2Widget(
115
179
  Role, "id", "name", order_by="name", default_value=("", _("- Select -"))
116
180
  ),
@@ -129,8 +193,24 @@ class User(Base):
129
193
  Role,
130
194
  secondary=user_role,
131
195
  secondaryjoin=Role.id == user_role.c.role_id,
132
- backref=backref("users", order_by="User.username", info={"colanderalchemy": {"exclude": True}}),
133
- info={"colanderalchemy": {"title": _("Roles"), "exclude": True}},
196
+ backref=backref(
197
+ "users",
198
+ order_by="User.username",
199
+ info={
200
+ "colanderalchemy": {
201
+ "title": _("Users"),
202
+ "description": _("Users granted with this role."),
203
+ "exclude": True,
204
+ }
205
+ },
206
+ ),
207
+ info={
208
+ "colanderalchemy": {
209
+ "title": _("Roles"),
210
+ "description": _("Roles granted to the user."),
211
+ "exclude": True,
212
+ }
213
+ },
134
214
  )
135
215
 
136
216
  last_login = Column(
@@ -138,25 +218,43 @@ class User(Base):
138
218
  info={
139
219
  "colanderalchemy": {
140
220
  "title": _("Last login"),
221
+ "description": _("Date of the user's last login."),
141
222
  "missing": drop,
142
223
  "widget": DateTimeInputWidget(readonly=True),
143
224
  }
144
225
  },
145
226
  )
146
227
 
147
- expire_on = Column(DateTime(timezone=True), info={"colanderalchemy": {"title": _("Expiration date")}})
228
+ expire_on = Column(
229
+ DateTime(timezone=True),
230
+ info={
231
+ "colanderalchemy": {
232
+ "title": _("Expiration date"),
233
+ "description": _("After this date the user will not be able to login anymore."),
234
+ }
235
+ },
236
+ )
148
237
 
149
- deactivated = Column(Boolean, default=False, info={"colanderalchemy": {"title": _("Deactivated")}})
238
+ deactivated = Column(
239
+ Boolean,
240
+ default=False,
241
+ info={
242
+ "colanderalchemy": {
243
+ "title": _("Deactivated"),
244
+ "description": _("Deactivate a user without removing it completely."),
245
+ }
246
+ },
247
+ )
150
248
 
151
- def __init__(
249
+ def __init__( # nosec
152
250
  self,
153
251
  username: str = "",
154
252
  password: str = "",
155
253
  email: str = "",
156
254
  is_password_changed: bool = False,
157
- settings_role: Role = None,
158
- roles: List[Role] = None,
159
- expire_on: datetime = None,
255
+ settings_role: Optional[Role] = None,
256
+ roles: Optional[List[Role]] = None,
257
+ expire_on: Optional[datetime] = None,
160
258
  deactivated: bool = False,
161
259
  ) -> None:
162
260
  self.username = username
@@ -172,16 +270,16 @@ class User(Base):
172
270
 
173
271
  @property
174
272
  def password(self) -> str:
175
- """returns password"""
176
- return self._password # pragma: no cover
273
+ """Get the password."""
274
+ return self._password # type: ignore
177
275
 
178
276
  @password.setter
179
277
  def password(self, password: str) -> None:
180
- """encrypts password on the fly."""
278
+ """Encrypt password on the fly."""
181
279
  self._password = self.__encrypt_password(password)
182
280
 
183
281
  def set_temp_password(self, password: str) -> None:
184
- """encrypts password on the fly."""
282
+ """Encrypt password on the fly."""
185
283
  self.temp_password = self.__encrypt_password(password)
186
284
 
187
285
  @staticmethod
@@ -194,8 +292,8 @@ class User(Base):
194
292
  return crypt.crypt(password, crypt.METHOD_SHA512)
195
293
 
196
294
  def validate_password(self, passwd: str) -> bool:
197
- """Check the password against existing credentials.
198
- this method _MUST_ return a boolean.
295
+ """
296
+ Check the password against existing credentials. this method _MUST_ return a boolean.
199
297
 
200
298
  @param passwd: the password that was provided by the user to
201
299
  try and authenticate. This is the clear text version that we will
@@ -214,7 +312,7 @@ class User(Base):
214
312
 
215
313
  if (
216
314
  self.temp_password is not None
217
- and self.temp_password != ""
315
+ and self.temp_password != "" # nosec
218
316
  and compare_hash(self.temp_password, crypt.crypt(passwd, self.temp_password))
219
317
  ):
220
318
  self._password = self.temp_password
@@ -230,10 +328,12 @@ class User(Base):
230
328
  self.last_login = datetime.now(pytz.utc)
231
329
 
232
330
  def __str__(self) -> str:
233
- return self.username or "" # pragma: no cover
331
+ return self.username or ""
234
332
 
235
333
 
236
- class Shorturl(Base):
334
+ class Shorturl(Base): # type: ignore
335
+ """The shorturl table representation."""
336
+
237
337
  __tablename__ = "shorturl"
238
338
  __table_args__ = {"schema": _schema}
239
339
  id = Column(Integer, primary_key=True)
@@ -245,18 +345,79 @@ class Shorturl(Base):
245
345
  nb_hits = Column(Integer)
246
346
 
247
347
 
248
- class OAuth2Client(Base):
348
+ class OAuth2Client(Base): # type: ignore
349
+ """The oauth2_client table representation."""
350
+
249
351
  __tablename__ = "oauth2_client"
250
352
  __table_args__ = {"schema": _schema}
251
353
  __colanderalchemy_config__ = {"title": _("OAuth2 Client"), "plural": _("OAuth2 Clients")}
252
354
  __c2cgeoform_config__ = {"duplicate": True}
253
355
  id = Column(Integer, primary_key=True, info={"colanderalchemy": {"widget": HiddenWidget()}})
254
- client_id = Column(Unicode, unique=True, info={"colanderalchemy": {"title": _("Client ID")}})
255
- secret = Column(Unicode, info={"colanderalchemy": {"title": _("Secret")}})
256
- redirect_uri = Column(Unicode, info={"colanderalchemy": {"title": _("Redirect URI")}})
356
+ client_id = Column(
357
+ Unicode,
358
+ unique=True,
359
+ info={
360
+ "colanderalchemy": {
361
+ "title": _("Client ID"),
362
+ "description": _("The client identifier as e.-g. 'qgis'."),
363
+ }
364
+ },
365
+ )
366
+ secret = Column(
367
+ Unicode,
368
+ info={
369
+ "colanderalchemy": {
370
+ "title": _("Secret"),
371
+ "description": _("The secret."),
372
+ }
373
+ },
374
+ )
375
+ redirect_uri = Column(
376
+ Unicode,
377
+ info={
378
+ "colanderalchemy": {
379
+ "title": _("Redirect URI"),
380
+ "description": _(
381
+ """
382
+ URI where user should be redirected after authentication
383
+ as e.-g. 'http://127.0.0.1:7070/' in case of QGIS desktop.
384
+ """
385
+ ),
386
+ }
387
+ },
388
+ )
389
+ state_required = Column(
390
+ Boolean,
391
+ default=False,
392
+ info={
393
+ "colanderalchemy": {
394
+ "title": _("State required"),
395
+ "description": _(
396
+ "The state is required for this client (see: "
397
+ "https://auth0.com/docs/secure/attack-protection/state-parameters)."
398
+ ),
399
+ }
400
+ },
401
+ )
402
+ pkce_required = Column(
403
+ Boolean,
404
+ default=False,
405
+ info={
406
+ "colanderalchemy": {
407
+ "title": _("PKCE required"),
408
+ "description": _(
409
+ "PKCE is required for this client (see: "
410
+ "https://auth0.com/docs/get-started/authentication-and-authorization-flow/"
411
+ "authorization-code-flow-with-proof-key-for-code-exchange-pkce)."
412
+ ),
413
+ }
414
+ },
415
+ )
257
416
 
258
417
 
259
- class OAuth2BearerToken(Base):
418
+ class OAuth2BearerToken(Base): # type: ignore
419
+ """The oauth2_bearertoken table representation."""
420
+
260
421
  __tablename__ = "oauth2_bearertoken"
261
422
  __table_args__ = (
262
423
  sqlalchemy.schema.UniqueConstraint("client_id", "user_id"),
@@ -272,9 +433,12 @@ class OAuth2BearerToken(Base):
272
433
  access_token = Column(Unicode(100), unique=True)
273
434
  refresh_token = Column(Unicode(100), unique=True)
274
435
  expire_at = Column(DateTime(timezone=True)) # in one hour
436
+ state = Column(String)
437
+
275
438
 
439
+ class OAuth2AuthorizationCode(Base): # type: ignore
440
+ """The oauth2_authorizationcode table representation."""
276
441
 
277
- class OAuth2AuthorizationCode(Base):
278
442
  __tablename__ = "oauth2_authorizationcode"
279
443
  __table_args__ = (
280
444
  sqlalchemy.schema.UniqueConstraint("client_id", "user_id"),
@@ -289,4 +453,18 @@ class OAuth2AuthorizationCode(Base):
289
453
  user = relationship(User)
290
454
  redirect_uri = Column(Unicode)
291
455
  code = Column(Unicode(100), unique=True)
456
+ state = Column(String)
457
+ challenge = Column(String(128))
458
+ challenge_method = Column(String(6))
292
459
  expire_at = Column(DateTime(timezone=True)) # in 10 minutes
460
+
461
+
462
+ class Log(AbstractLog):
463
+ """The static log table representation."""
464
+
465
+ __tablename__ = "log"
466
+ __table_args__ = {"schema": _schema}
467
+ __mapper_args__ = {
468
+ "polymorphic_identity": "static",
469
+ "concrete": True,
470
+ }
File without changes
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2018-2020, Camptocamp SA
1
+ # Copyright (c) 2018-2023, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -28,6 +26,8 @@
28
26
  # either expressed or implied, of the FreeBSD Project.
29
27
 
30
28
 
29
+ from typing import Any, Dict
30
+
31
31
  import zope.sqlalchemy
32
32
  from sqlalchemy import engine_from_config
33
33
  from sqlalchemy.engine import Engine
@@ -35,11 +35,13 @@ from sqlalchemy.orm import Session, configure_mappers, sessionmaker
35
35
  from transaction import TransactionManager
36
36
 
37
37
 
38
- def get_engine(settings: dict, prefix: str = "sqlalchemy.") -> Engine:
38
+ def get_engine(settings: Dict[str, Any], prefix: str = "sqlalchemy.") -> Engine:
39
+ """Get the engine."""
39
40
  return engine_from_config(settings, prefix)
40
41
 
41
42
 
42
43
  def get_session_factory(engine: Engine) -> sessionmaker:
44
+ """Get the session factory."""
43
45
  factory = sessionmaker()
44
46
  factory.configure(bind=engine)
45
47
  return factory
@@ -64,7 +66,6 @@ def get_tm_session(session_factory: sessionmaker, transaction_manager: Transacti
64
66
  session_factory = get_session_factory(engine)
65
67
  with transaction.manager:
66
68
  dbsession = get_tm_session(session_factory, transaction.manager)
67
-
68
69
  """
69
70
  dbsession = session_factory()
70
71
  zope.sqlalchemy.register(dbsession, transaction_manager=transaction_manager)
@@ -72,13 +73,12 @@ def get_tm_session(session_factory: sessionmaker, transaction_manager: Transacti
72
73
 
73
74
 
74
75
  def generate_mappers() -> None:
75
- """
76
- Initialize the model for a Pyramid app.
77
- """
76
+ """Initialize the model for a Pyramid app."""
78
77
 
79
78
  # import or define all models here to ensure they are attached to the
80
79
  # Base.metadata prior to any initialization routines
81
- import c2cgeoportal_commons.models.main # noqa: F401
80
+ import c2cgeoportal_commons.models.main # pylint: disable=unused-import,import-outside-toplevel
81
+ import c2cgeoportal_commons.models.static # pylint: disable=import-outside-toplevel
82
82
 
83
83
  # run configure_mappers after defining all of the models to ensure
84
84
  # all relationships can be setup
@@ -86,8 +86,9 @@ def generate_mappers() -> None:
86
86
 
87
87
 
88
88
  def get_session(
89
- settings: dict, transaction_manager: TransactionManager, prefix: str = "sqlalchemy."
89
+ settings: Dict[str, Any], transaction_manager: TransactionManager, prefix: str = "sqlalchemy."
90
90
  ) -> Session:
91
+ """Get the session."""
91
92
  configure_mappers()
92
93
  engine = get_engine(settings, prefix)
93
94
  session_factory = get_session_factory(engine)
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2017-2020, Camptocamp SA
1
+ # Copyright (c) 2017-2021, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -30,7 +28,7 @@
30
28
 
31
29
  import os
32
30
  import sys
33
- from typing import List
31
+ from typing import List, cast
34
32
 
35
33
  from sqlalchemy.engine import Connection
36
34
  from sqlalchemy.orm import Session
@@ -39,30 +37,32 @@ from c2cgeoportal_commons.models import Base
39
37
 
40
38
 
41
39
  def usage(argv: List[str]) -> None:
40
+ """Get the usage."""
42
41
  cmd = os.path.basename(argv[0])
43
- print("usage: %s <config_uri> [var=value]\n" '(example: "%s development.ini")' % (cmd, cmd))
42
+ print(f"usage: {cmd} <config_uri> [var=value]\n" '(example: "{cmd} development.ini")')
44
43
  sys.exit(1)
45
44
 
46
45
 
47
46
  def schema_exists(connection: Connection, schema_name: str) -> bool:
48
- sql = """
47
+ """Get if the schema exist."""
48
+ sql = f"""
49
49
  SELECT count(*) AS count
50
50
  FROM information_schema.schemata
51
- WHERE schema_name = '{}';
52
- """.format(
53
- schema_name
54
- )
51
+ WHERE schema_name = '{schema_name}';
52
+ """
55
53
  result = connection.execute(sql)
56
54
  row = result.first()
57
- return row[0] == 1
55
+ return cast(bool, row[0] == 1)
58
56
 
59
57
 
60
58
  def truncate_tables(connection: Connection) -> None:
59
+ """Truncate all the tables defined in the model."""
61
60
  for t in Base.metadata.sorted_tables:
62
- connection.execute("TRUNCATE TABLE {}.{} CASCADE;".format(t.schema, t.name))
61
+ connection.execute(f"TRUNCATE TABLE {t.schema}.{t.name} CASCADE;")
63
62
 
64
63
 
65
64
  def setup_test_data(dbsession: Session) -> None:
65
+ """Initialize the testing data."""
66
66
  from c2cgeoportal_commons.models.main import Role # pylint: disable=import-outside-toplevel
67
67
  from c2cgeoportal_commons.models.static import User # pylint: disable=import-outside-toplevel
68
68
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: c2cgeoportal-commons
3
- Version: 2.6.0
3
+ Version: 2.8.1.180
4
4
  Summary: c2cgeoportal commons
5
5
  Home-page: https://github.com/camptocamp/c2cgeoportal/
6
6
  Author: Camptocamp
@@ -8,19 +8,24 @@ Author-email: info@camptocamp.com
8
8
  License: UNKNOWN
9
9
  Keywords: web gis geoportail c2cgeoportal geocommune pyramid
10
10
  Platform: UNKNOWN
11
- Classifier: Programming Language :: Python
12
- Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.7
11
+ Classifier: Development Status :: 6 - Mature
12
+ Classifier: Environment :: Web Environment
14
13
  Classifier: Framework :: Pyramid
15
- Classifier: Topic :: Internet :: WWW/HTTP
16
- Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
14
+ Classifier: Intended Audience :: Other Audience
17
15
  Classifier: License :: OSI Approved :: BSD License
18
- Classifier: Development Status :: 6 - Mature
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.8
20
+ Classifier: Topic :: Scientific/Engineering :: GIS
21
+ Classifier: Typing :: Typed
19
22
  Description-Content-Type: text/markdown
20
23
  Requires-Dist: GeoAlchemy2
21
24
  Requires-Dist: c2c.template
25
+ Requires-Dist: jinja2 (>=3.1.3)
22
26
  Requires-Dist: papyrus
23
27
  Requires-Dist: pyramid
28
+ Requires-Dist: setuptools (>=65.5.1)
24
29
  Requires-Dist: sqlalchemy
25
30
  Requires-Dist: transaction
26
31
  Requires-Dist: zope.event
@@ -30,14 +35,16 @@ Provides-Extra: testing
30
35
  Requires-Dist: transaction ; extra == 'testing'
31
36
  Provides-Extra: upgrade
32
37
  Requires-Dist: alembic ; extra == 'upgrade'
33
- Requires-Dist: psycopg2-binary ; extra == 'upgrade'
38
+ Requires-Dist: psycopg2 ; extra == 'upgrade'
34
39
 
35
40
  # c2cgeoportal_common
36
41
 
37
42
  ## Checkout
38
43
 
44
+ ```
39
45
  git clone git@github.com:camptocamp/c2cgeoportal.git
40
46
  cd common
47
+ ```
41
48
 
42
49
  ## Create virtual environment
43
50
 
@@ -51,11 +58,9 @@ sudo -u postgres psql -c "CREATE USER \"www-data\" WITH PASSWORD 'www-data';"
51
58
  DATABASE=geomapfish_tests
52
59
  sudo -u postgres psql -c "CREATE DATABASE $DATABASE WITH OWNER \"www-data\";"
53
60
  sudo -u postgres psql -d $DATABASE -c "CREATE EXTENSION postgis;"
54
-
55
61
  ```
56
- use common/testing/initialized.py to create the database (development.ini at admin side)
57
62
 
58
- ```
63
+ use `common/testing/initialized.py` to create the database (`development.ini` at admin side)
59
64
 
60
65
  ## Run the tests
61
66