ocd-auth 1.0.0__tar.gz

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.
@@ -0,0 +1,4 @@
1
+ .venv/
2
+ .env
3
+ .idea/
4
+ .idea/*
@@ -0,0 +1,5 @@
1
+ # Default ignored files
2
+ /shelf/
3
+ /workspace.xml
4
+ # Editor-based HTTP Client requests
5
+ /httpRequests/
@@ -0,0 +1,6 @@
1
+ <component name="InspectionProjectProfileManager">
2
+ <settings>
3
+ <option name="USE_PROJECT_PROFILE" value="false" />
4
+ <version value="1.0" />
5
+ </settings>
6
+ </component>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="Black">
4
+ <option name="sdkName" value="Python 3.14 (ordcauth)" />
5
+ </component>
6
+ </project>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/ordcauth.iml" filepath="$PROJECT_DIR$/.idea/ordcauth.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="PYTHON_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$">
5
+ <sourceFolder url="file://$MODULE_DIR$/src/ocd_auth" isTestSource="false" />
6
+ <sourceFolder url="file://$MODULE_DIR$/src/ocd_auth/dtos" isTestSource="false" />
7
+ <excludeFolder url="file://$MODULE_DIR$/.venv" />
8
+ </content>
9
+ <orderEntry type="jdk" jdkName="Python 3.14 (ordcauth)" jdkType="Python SDK" />
10
+ <orderEntry type="sourceFolder" forTests="false" />
11
+ </component>
12
+ </module>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
5
+ </component>
6
+ </project>
@@ -0,0 +1,193 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ChangeListManager">
4
+ <list default="true" id="49ac04b5-136f-4924-a4a1-ed61e8d10c1d" name="Changes" comment="chore: configuration du projet pour la publication via pyproject.toml">
5
+ <change afterPath="$PROJECT_DIR$/.idea/.gitignore" afterDir="false" />
6
+ <change afterPath="$PROJECT_DIR$/.idea/inspectionProfiles/profiles_settings.xml" afterDir="false" />
7
+ <change afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
8
+ <change afterPath="$PROJECT_DIR$/.idea/modules.xml" afterDir="false" />
9
+ <change afterPath="$PROJECT_DIR$/.idea/ordcauth.iml" afterDir="false" />
10
+ <change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
11
+ <change beforePath="$PROJECT_DIR$/main.py" beforeDir="false" />
12
+ <change beforePath="$PROJECT_DIR$/src/__init__.py" beforeDir="false" />
13
+ <change beforePath="$PROJECT_DIR$/src/ocd_auth/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/ocd_auth/__init__.py" afterDir="false" />
14
+ <change beforePath="$PROJECT_DIR$/src/ocd_auth/main.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/ocd_auth/main.py" afterDir="false" />
15
+ </list>
16
+ <option name="SHOW_DIALOG" value="false" />
17
+ <option name="HIGHLIGHT_CONFLICTS" value="true" />
18
+ <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
19
+ <option name="LAST_RESOLUTION" value="IGNORE" />
20
+ </component>
21
+ <component name="FileTemplateManagerImpl">
22
+ <option name="RECENT_TEMPLATES">
23
+ <list>
24
+ <option value="Python Script" />
25
+ </list>
26
+ </option>
27
+ </component>
28
+ <component name="Git.Settings">
29
+ <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
30
+ </component>
31
+ <component name="ProjectColorInfo">{
32
+ &quot;associatedIndex&quot;: 2,
33
+ &quot;fromUser&quot;: false
34
+ }</component>
35
+ <component name="ProjectId" id="3I5GCwmCLGeBa0cQVqGAzHuVlm5" />
36
+ <component name="ProjectViewState">
37
+ <option name="hideEmptyMiddlePackages" value="true" />
38
+ <option name="showLibraryContents" value="true" />
39
+ </component>
40
+ <component name="PropertiesComponent"><![CDATA[{
41
+ "keyToString": {
42
+ "ModuleVcsDetector.initialDetectionPerformed": "true",
43
+ "Python tests.Python tests for tests.test_ldap_client_integration.test_authenticate_avec_le_vrai_serveur_ldap.executor": "Run",
44
+ "Python tests.Python tests for tests.test_ldap_client_integration.test_authenticate_ldap.executor": "Run",
45
+ "RunOnceActivity.ShowReadmeOnStart": "true",
46
+ "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
47
+ "RunOnceActivity.git.unshallow": "true",
48
+ "RunOnceActivity.typescript.service.memoryLimit.init": "true",
49
+ "codeWithMe.voiceChat.enabledByDefault": "false",
50
+ "git-widget-placeholder": "main",
51
+ "last_opened_file_path": "C:/Users/WPKW8206/PycharmProjects/ordcauth/src/ocd_auth/clients",
52
+ "nodejs_package_manager_path": "npm"
53
+ }
54
+ }]]></component>
55
+ <component name="RecentsManager">
56
+ <key name="CopyFile.RECENT_KEYS">
57
+ <recent name="C:\Users\WPKW8206\PycharmProjects\ordcauth\src\ocd_auth\clients" />
58
+ </key>
59
+ <key name="MoveFile.RECENT_KEYS">
60
+ <recent name="C:\Users\WPKW8206\PycharmProjects\ordcauth\src\ocd_auth\dtos" />
61
+ <recent name="C:\Users\WPKW8206\PycharmProjects\ordcauth\src\ocd_auth$\dtos\requests" />
62
+ <recent name="C:\Users\WPKW8206\PycharmProjects\ordcauth\src\ocd_auth$\dtos\responses" />
63
+ <recent name="C:\Users\WPKW8206\PycharmProjects\ordcauth\src\ocd_auth$" />
64
+ <recent name="C:\Users\WPKW8206\PycharmProjects\ordcauth\src\ocd_auth\dto\requests" />
65
+ </key>
66
+ </component>
67
+ <component name="RunManager" selected="Python tests.Python tests for tests.test_ldap_client_integration.test_authenticate_ldap">
68
+ <configuration name="main" type="PythonConfigurationType" factoryName="Python" nameIsGenerated="true">
69
+ <module name="ordcauth" />
70
+ <option name="ENV_FILES" value="" />
71
+ <option name="INTERPRETER_OPTIONS" value="" />
72
+ <option name="PARENT_ENVS" value="true" />
73
+ <envs>
74
+ <env name="PYTHONUNBUFFERED" value="1" />
75
+ </envs>
76
+ <option name="SDK_HOME" value="" />
77
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
78
+ <option name="IS_MODULE_SDK" value="true" />
79
+ <option name="ADD_CONTENT_ROOTS" value="true" />
80
+ <option name="ADD_SOURCE_ROOTS" value="true" />
81
+ <option name="DEBUG_JUST_MY_CODE" value="false" />
82
+ <option name="RUN_TOOL" value="" />
83
+ <option name="SCRIPT_NAME" value="$PROJECT_DIR$/main.py" />
84
+ <option name="PARAMETERS" value="" />
85
+ <option name="SHOW_COMMAND_LINE" value="false" />
86
+ <option name="EMULATE_TERMINAL" value="false" />
87
+ <option name="MODULE_MODE" value="false" />
88
+ <option name="REDIRECT_INPUT" value="false" />
89
+ <option name="INPUT_FILE" value="" />
90
+ <method v="2" />
91
+ </configuration>
92
+ <configuration name="Python tests for tests.test_ldap_client_integration.test_authenticate_avec_le_vrai_serveur_ldap" type="tests" factoryName="Autodetect" temporary="true" nameIsGenerated="true">
93
+ <module name="ordcauth" />
94
+ <option name="ENV_FILES" value="" />
95
+ <option name="INTERPRETER_OPTIONS" value="" />
96
+ <option name="PARENT_ENVS" value="true" />
97
+ <option name="SDK_HOME" value="" />
98
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
99
+ <option name="IS_MODULE_SDK" value="true" />
100
+ <option name="ADD_CONTENT_ROOTS" value="true" />
101
+ <option name="ADD_SOURCE_ROOTS" value="true" />
102
+ <option name="DEBUG_JUST_MY_CODE" value="false" />
103
+ <option name="RUN_TOOL" value="" />
104
+ <option name="_new_additionalArguments" value="&quot;&quot;" />
105
+ <option name="_new_target" value="&quot;tests.test_ldap_client_integration.test_authenticate_avec_le_vrai_serveur_ldap&quot;" />
106
+ <option name="_new_targetType" value="&quot;PYTHON&quot;" />
107
+ <method v="2" />
108
+ </configuration>
109
+ <configuration name="Python tests for tests.test_ldap_client_integration.test_authenticate_ldap" type="tests" factoryName="Autodetect" temporary="true" nameIsGenerated="true">
110
+ <module name="ordcauth" />
111
+ <option name="ENV_FILES" value="" />
112
+ <option name="INTERPRETER_OPTIONS" value="" />
113
+ <option name="PARENT_ENVS" value="true" />
114
+ <option name="SDK_HOME" value="" />
115
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
116
+ <option name="IS_MODULE_SDK" value="true" />
117
+ <option name="ADD_CONTENT_ROOTS" value="true" />
118
+ <option name="ADD_SOURCE_ROOTS" value="true" />
119
+ <option name="DEBUG_JUST_MY_CODE" value="false" />
120
+ <option name="RUN_TOOL" value="" />
121
+ <option name="_new_additionalArguments" value="&quot;&quot;" />
122
+ <option name="_new_target" value="&quot;tests.test_ldap_client_integration.test_authenticate_ldap&quot;" />
123
+ <option name="_new_targetType" value="&quot;PYTHON&quot;" />
124
+ <method v="2" />
125
+ </configuration>
126
+ <recent_temporary>
127
+ <list>
128
+ <item itemvalue="Python tests.Python tests for tests.test_ldap_client_integration.test_authenticate_ldap" />
129
+ <item itemvalue="Python tests.Python tests for tests.test_ldap_client_integration.test_authenticate_avec_le_vrai_serveur_ldap" />
130
+ </list>
131
+ </recent_temporary>
132
+ </component>
133
+ <component name="SharedIndexes">
134
+ <attachedChunks>
135
+ <set>
136
+ <option value="bundled-python-sdk-c59985aa861c-c2ffad84badb-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-261.24374.152" />
137
+ </set>
138
+ </attachedChunks>
139
+ </component>
140
+ <component name="TaskManager">
141
+ <task active="true" id="Default" summary="Default task">
142
+ <changelist id="49ac04b5-136f-4924-a4a1-ed61e8d10c1d" name="Changes" comment="" />
143
+ <created>1787046891910</created>
144
+ <option name="number" value="Default" />
145
+ <option name="presentableId" value="Default" />
146
+ <updated>1787046891910</updated>
147
+ </task>
148
+ <task id="LOCAL-00001" summary="feat: implémentation du service LdapClient pour l'authentification LDAP">
149
+ <option name="closed" value="true" />
150
+ <created>1787051749429</created>
151
+ <option name="number" value="00001" />
152
+ <option name="presentableId" value="LOCAL-00001" />
153
+ <option name="project" value="LOCAL" />
154
+ <updated>1787051749429</updated>
155
+ </task>
156
+ <task id="LOCAL-00002" summary="feat: implémentation du NotificationService pour l'envoi de SMS et d'e-mails">
157
+ <option name="closed" value="true" />
158
+ <created>1787053436581</created>
159
+ <option name="number" value="00002" />
160
+ <option name="presentableId" value="LOCAL-00002" />
161
+ <option name="project" value="LOCAL" />
162
+ <updated>1787053436581</updated>
163
+ </task>
164
+ <task id="LOCAL-00003" summary="feat: implémentation de l'AuthService pour encapsuler LdapClient et NotificationService">
165
+ <option name="closed" value="true" />
166
+ <created>1787060609164</created>
167
+ <option name="number" value="00003" />
168
+ <option name="presentableId" value="LOCAL-00003" />
169
+ <option name="project" value="LOCAL" />
170
+ <updated>1787060609165</updated>
171
+ </task>
172
+ <task id="LOCAL-00004" summary="chore: configuration du projet pour la publication via pyproject.toml">
173
+ <option name="closed" value="true" />
174
+ <created>1787061404156</created>
175
+ <option name="number" value="00004" />
176
+ <option name="presentableId" value="LOCAL-00004" />
177
+ <option name="project" value="LOCAL" />
178
+ <updated>1787061404156</updated>
179
+ </task>
180
+ <option name="localTasksCounter" value="5" />
181
+ <servers />
182
+ </component>
183
+ <component name="TypeScriptGeneratedFilesManager">
184
+ <option name="version" value="3" />
185
+ </component>
186
+ <component name="VcsManagerConfiguration">
187
+ <MESSAGE value="feat: implémentation du service LdapClient pour l'authentification LDAP" />
188
+ <MESSAGE value="feat: implémentation du NotificationService pour l'envoi de SMS et d'e-mails" />
189
+ <MESSAGE value="feat: implémentation de l'AuthService pour encapsuler LdapClient et NotificationService" />
190
+ <MESSAGE value="chore: configuration du projet pour la publication via pyproject.toml" />
191
+ <option name="LAST_COMMIT_MESSAGE" value="chore: configuration du projet pour la publication via pyproject.toml" />
192
+ </component>
193
+ </project>
@@ -0,0 +1,147 @@
1
+ Metadata-Version: 2.5
2
+ Name: ocd-auth
3
+ Version: 1.0.0
4
+ Summary: Module d'authentification LDAP et d'envoi de notifications par SMS ou e-mail.
5
+ Author-email: Zgenius Matondo <zgeniuscoders@gmail.com>
6
+ Requires-Python: >=3.9
7
+ Requires-Dist: requests>=2.34.2
8
+ Description-Content-Type: text/markdown
9
+
10
+ # AuthService
11
+
12
+ Service permettant d’authentifier un utilisateur auprès d’un serveur LDAP et d’envoyer un message par SMS ou par e-mail.
13
+
14
+ ## Fonctionnement
15
+
16
+ Après une authentification LDAP réussie :
17
+
18
+ - un SMS est envoyé si le numéro de téléphone est disponible ;
19
+ - sinon, un e-mail est envoyé si l’adresse e-mail est disponible ;
20
+ - un objet `AuthResponse` est retourné avec les informations de l’utilisateur.
21
+
22
+ ## Importer le service
23
+
24
+ ```python
25
+ from auth_service import AuthService
26
+ ```
27
+
28
+ ## Initialiser `AuthService`
29
+
30
+ ```python
31
+ auth_service = AuthService(
32
+ ldap_base_url="https://ldap.example.com",
33
+ notification_base_url="https://notification.example.com",
34
+ app_name="my-application",
35
+ )
36
+ ```
37
+
38
+ ### Paramètres du constructeur
39
+
40
+ | Paramètre | Type | Description |
41
+ |---|---|---|
42
+ | `ldap_base_url` | `str` | URL de base du service LDAP |
43
+ | `notification_base_url` | `str` | URL de base du service de notification |
44
+ | `app_name` | `str` | Nom de l’application |
45
+
46
+ ## Authentifier un utilisateur
47
+
48
+ ```python
49
+ response = auth_service.authenticate(
50
+ cuid="user123",
51
+ password="password",
52
+ message="Votre code de vérification est : 123456",
53
+ )
54
+ ```
55
+
56
+ Par défaut :
57
+
58
+ - le nom de l’expéditeur SMS est `Orange` ;
59
+ - l’adresse e-mail d’envoi est `no-reply@orange.com` ;
60
+ - l’objet de l’e-mail est `OTP`.
61
+
62
+ ## Personnaliser l’envoi
63
+
64
+ ```python
65
+ response = auth_service.authenticate(
66
+ cuid="user123",
67
+ password="password",
68
+ message="Votre code de vérification est : 123456",
69
+ sms_sender_name="MonApplication",
70
+ email_sender_address="support@example.com",
71
+ email_subject="Code de vérification",
72
+ )
73
+ ```
74
+
75
+ ## Utiliser la réponse
76
+
77
+ La méthode `authenticate` retourne un objet `AuthResponse`.
78
+
79
+ ```python
80
+ print(response.cuid)
81
+ print(response.email)
82
+ print(response.phone_number)
83
+ ```
84
+
85
+ Exemple de réponse :
86
+
87
+ ```python
88
+ {
89
+ "cuid": "user123",
90
+ "email": "user123@example.com",
91
+ "phone_number": "+2250102030405"
92
+ }
93
+ ```
94
+
95
+ ## Gestion des erreurs
96
+
97
+ Une exception est levée lorsque les identifiants sont incorrects ou que l’authentification LDAP échoue.
98
+
99
+ ```python
100
+ try:
101
+ response = auth_service.authenticate(
102
+ cuid="user123",
103
+ password="wrong-password",
104
+ message="Votre code de vérification est : 123456",
105
+ )
106
+
107
+ print("Authentification réussie")
108
+ print(response)
109
+
110
+ except Exception as error:
111
+ print(f"Échec de l'authentification : {error}")
112
+ ```
113
+
114
+ ## Exemple complet
115
+
116
+ ```python
117
+ from auth_service import AuthService
118
+
119
+
120
+ def main() -> None:
121
+ auth_service = AuthService(
122
+ ldap_base_url="https://ldap.example.com",
123
+ notification_base_url="https://notification.example.com",
124
+ app_name="my-application",
125
+ )
126
+
127
+ try:
128
+ response = auth_service.authenticate(
129
+ cuid="user123",
130
+ password="password",
131
+ message="Votre code OTP est : 123456",
132
+ )
133
+
134
+ print(f"Authentification réussie pour : {response.cuid}")
135
+
136
+ if response.phone_number:
137
+ print(f"Notification envoyée au numéro : {response.phone_number}")
138
+ elif response.email:
139
+ print(f"Notification envoyée à l'adresse : {response.email}")
140
+
141
+ except Exception as error:
142
+ print(f"Erreur : {error}")
143
+
144
+
145
+ if __name__ == "__main__":
146
+ main()
147
+ ```
@@ -0,0 +1,138 @@
1
+ # AuthService
2
+
3
+ Service permettant d’authentifier un utilisateur auprès d’un serveur LDAP et d’envoyer un message par SMS ou par e-mail.
4
+
5
+ ## Fonctionnement
6
+
7
+ Après une authentification LDAP réussie :
8
+
9
+ - un SMS est envoyé si le numéro de téléphone est disponible ;
10
+ - sinon, un e-mail est envoyé si l’adresse e-mail est disponible ;
11
+ - un objet `AuthResponse` est retourné avec les informations de l’utilisateur.
12
+
13
+ ## Importer le service
14
+
15
+ ```python
16
+ from auth_service import AuthService
17
+ ```
18
+
19
+ ## Initialiser `AuthService`
20
+
21
+ ```python
22
+ auth_service = AuthService(
23
+ ldap_base_url="https://ldap.example.com",
24
+ notification_base_url="https://notification.example.com",
25
+ app_name="my-application",
26
+ )
27
+ ```
28
+
29
+ ### Paramètres du constructeur
30
+
31
+ | Paramètre | Type | Description |
32
+ |---|---|---|
33
+ | `ldap_base_url` | `str` | URL de base du service LDAP |
34
+ | `notification_base_url` | `str` | URL de base du service de notification |
35
+ | `app_name` | `str` | Nom de l’application |
36
+
37
+ ## Authentifier un utilisateur
38
+
39
+ ```python
40
+ response = auth_service.authenticate(
41
+ cuid="user123",
42
+ password="password",
43
+ message="Votre code de vérification est : 123456",
44
+ )
45
+ ```
46
+
47
+ Par défaut :
48
+
49
+ - le nom de l’expéditeur SMS est `Orange` ;
50
+ - l’adresse e-mail d’envoi est `no-reply@orange.com` ;
51
+ - l’objet de l’e-mail est `OTP`.
52
+
53
+ ## Personnaliser l’envoi
54
+
55
+ ```python
56
+ response = auth_service.authenticate(
57
+ cuid="user123",
58
+ password="password",
59
+ message="Votre code de vérification est : 123456",
60
+ sms_sender_name="MonApplication",
61
+ email_sender_address="support@example.com",
62
+ email_subject="Code de vérification",
63
+ )
64
+ ```
65
+
66
+ ## Utiliser la réponse
67
+
68
+ La méthode `authenticate` retourne un objet `AuthResponse`.
69
+
70
+ ```python
71
+ print(response.cuid)
72
+ print(response.email)
73
+ print(response.phone_number)
74
+ ```
75
+
76
+ Exemple de réponse :
77
+
78
+ ```python
79
+ {
80
+ "cuid": "user123",
81
+ "email": "user123@example.com",
82
+ "phone_number": "+2250102030405"
83
+ }
84
+ ```
85
+
86
+ ## Gestion des erreurs
87
+
88
+ Une exception est levée lorsque les identifiants sont incorrects ou que l’authentification LDAP échoue.
89
+
90
+ ```python
91
+ try:
92
+ response = auth_service.authenticate(
93
+ cuid="user123",
94
+ password="wrong-password",
95
+ message="Votre code de vérification est : 123456",
96
+ )
97
+
98
+ print("Authentification réussie")
99
+ print(response)
100
+
101
+ except Exception as error:
102
+ print(f"Échec de l'authentification : {error}")
103
+ ```
104
+
105
+ ## Exemple complet
106
+
107
+ ```python
108
+ from auth_service import AuthService
109
+
110
+
111
+ def main() -> None:
112
+ auth_service = AuthService(
113
+ ldap_base_url="https://ldap.example.com",
114
+ notification_base_url="https://notification.example.com",
115
+ app_name="my-application",
116
+ )
117
+
118
+ try:
119
+ response = auth_service.authenticate(
120
+ cuid="user123",
121
+ password="password",
122
+ message="Votre code OTP est : 123456",
123
+ )
124
+
125
+ print(f"Authentification réussie pour : {response.cuid}")
126
+
127
+ if response.phone_number:
128
+ print(f"Notification envoyée au numéro : {response.phone_number}")
129
+ elif response.email:
130
+ print(f"Notification envoyée à l'adresse : {response.email}")
131
+
132
+ except Exception as error:
133
+ print(f"Erreur : {error}")
134
+
135
+
136
+ if __name__ == "__main__":
137
+ main()
138
+ ```
@@ -0,0 +1,19 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "ocd-auth"
7
+ version = "1.0.0"
8
+ description = "Module d'authentification LDAP et d'envoi de notifications par SMS ou e-mail."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ authors = [
12
+ { name = "Zgenius Matondo", email = "zgeniuscoders@gmail.com" }
13
+ ]
14
+ dependencies = [
15
+ "requests>=2.34.2"
16
+ ]
17
+
18
+ [tool.hatch.build.targets.wheel]
19
+ packages = ["src/ocd_auth"]
Binary file
@@ -0,0 +1,8 @@
1
+ from .dtos.login_request import LoginRequest
2
+ from .dtos.login_response import LoginResponse
3
+ from .clients.ldap_client import LdapClient
4
+ from .enums.ldap_status_enum import LDAPStatusEnum
5
+ from .services.auth_service import AuthService
6
+
7
+ __version__ = "1.0.0"
8
+ __all__ = ["LoginResponse", "LoginRequest", "LdapClient", "LDAPStatusEnum", "AuthService"]
@@ -0,0 +1,4 @@
1
+ from . import ldap_client
2
+ from . import notification_client
3
+
4
+ __all__ = ["ldap_client", "notification_client"]
@@ -0,0 +1,33 @@
1
+ import requests
2
+
3
+ from dtos import LoginRequest, LoginResponse
4
+
5
+
6
+ class LdapClient:
7
+
8
+ def __init__(
9
+ self,
10
+ base_url: str,
11
+ timeout: int = 10,
12
+ ) -> None:
13
+ self.base_url = base_url.rstrip("/")
14
+ self.timeout = timeout
15
+
16
+ def authenticate(self, request: LoginRequest) -> LoginResponse:
17
+ url = f"{self.base_url}/"
18
+
19
+ headers = {
20
+ "Content-Type": "application/xml",
21
+ "Accept": "application/xml",
22
+ }
23
+
24
+ response = requests.post(
25
+ url=url,
26
+ data=request.to_xml(),
27
+ headers=headers,
28
+ timeout=self.timeout,
29
+ )
30
+
31
+ response.raise_for_status()
32
+
33
+ return LoginResponse.from_xml(response.text)
@@ -0,0 +1,43 @@
1
+ import requests
2
+
3
+ from dtos import SmsRequest, EmailRequest
4
+
5
+
6
+ class NotificationClient:
7
+ def __init__(
8
+ self,
9
+ base_url: str,
10
+ timeout: int = 15,
11
+ ) -> None:
12
+ self.base_url = base_url.rstrip("/")
13
+ self.timeout = timeout
14
+
15
+ def send_sms(self, request: SmsRequest) -> None:
16
+ url = f"{self.base_url}/sms"
17
+
18
+ response = requests.post(
19
+ url=url,
20
+ json=request.to_dict(),
21
+ headers={
22
+ "Accept": "application/json",
23
+ "Content-Type": "application/json",
24
+ },
25
+ timeout=self.timeout,
26
+ )
27
+
28
+ response.raise_for_status()
29
+
30
+ def send_mail(self, request: EmailRequest) -> None:
31
+ url = f"{self.base_url}/mail"
32
+
33
+ response = requests.post(
34
+ url=url,
35
+ json=request.to_dict(),
36
+ headers={
37
+ "Accept": "application/json",
38
+ "Content-Type": "application/json",
39
+ },
40
+ timeout=self.timeout,
41
+ )
42
+
43
+ response.raise_for_status()
@@ -0,0 +1,15 @@
1
+ from .login_response import LoginResponse
2
+ from .login_request import LoginRequest
3
+ from .sms_request import SmsRequest
4
+ from .email_attachment import EmailAttachment
5
+ from .email_request import EmailRequest
6
+ from .auth_response import AuthResponse
7
+
8
+ __all__ = [
9
+ "LoginResponse",
10
+ "LoginRequest",
11
+ "SmsRequest",
12
+ "EmailRequest",
13
+ "EmailAttachment",
14
+ "AuthResponse"
15
+ ]
@@ -0,0 +1,8 @@
1
+ from typing import Optional
2
+
3
+
4
+ class AuthResponse:
5
+ def __init__(self, cuid: str, email: Optional[str] = None, phone_number: Optional[str] = None):
6
+ self.email = email
7
+ self.cuid = cuid
8
+ self.phone_number = phone_number
@@ -0,0 +1,14 @@
1
+ from dataclasses import dataclass
2
+ from typing import Any, Dict
3
+
4
+
5
+ @dataclass
6
+ class EmailAttachment:
7
+ filename: str
8
+ content: str
9
+
10
+ def to_dict(self) -> Dict[str, Any]:
11
+ return {
12
+ "filename": self.filename,
13
+ "content": self.content,
14
+ }
@@ -0,0 +1,27 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import Any, Dict, List
3
+
4
+ from dtos import EmailAttachment
5
+
6
+
7
+ @dataclass
8
+ class EmailRequest:
9
+ sender: str
10
+ recipients: List[str]
11
+ subject: str
12
+ text: str
13
+ html: str
14
+ attachments: List[EmailAttachment] = field(default_factory=list)
15
+
16
+ def to_dict(self) -> Dict[str, Any]:
17
+ return {
18
+ "from": self.sender,
19
+ "to": self.recipients,
20
+ "subject": self.subject,
21
+ "text": self.text,
22
+ "html": self.html,
23
+ "attachments": [
24
+ attachment.to_dict()
25
+ for attachment in self.attachments
26
+ ],
27
+ }
@@ -0,0 +1,36 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+ from xml.etree.ElementTree import Element, SubElement, tostring
4
+
5
+ @dataclass
6
+ class LoginRequest:
7
+ app_name: str
8
+ cuid: str
9
+ password: str
10
+ date: Optional[str] = None
11
+ type: Optional[str] = "AUTH_SVC"
12
+
13
+ def to_xml(self) -> str:
14
+ root = Element("COMMAND")
15
+
16
+ self._add_element(root, "TYPE", self.type)
17
+ self._add_element(root, "APPLINAME", self.app_name)
18
+ self._add_element(root, "CUID", self.cuid)
19
+ self._add_element(root, "PASSWORD", self.password)
20
+ self._add_element(root, "DATE", self.date)
21
+
22
+ return tostring(
23
+ root,
24
+ encoding="unicode",
25
+ xml_declaration=False,
26
+ )
27
+
28
+ @staticmethod
29
+ def _add_element(
30
+ parent: Element,
31
+ tag: str,
32
+ value: Optional[str],
33
+ ) -> None:
34
+ if value is not None:
35
+ element = SubElement(parent, tag)
36
+ element.text = value
@@ -0,0 +1,59 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+ from xml.etree import ElementTree
4
+
5
+ from enums.ldap_status_enum import LDAPStatusEnum
6
+
7
+
8
+ @dataclass
9
+ class LoginResponse:
10
+ type: Optional[str] = None
11
+ app_name: Optional[str] = None
12
+ request_status: Optional[LDAPStatusEnum] = None
13
+ date: Optional[str] = None
14
+ cuid: Optional[str] = None
15
+ full_name: Optional[str] = None
16
+ name: Optional[str] = None
17
+ firstname: Optional[str] = None
18
+ description: Optional[str] = None
19
+ phone_number: Optional[str] = None
20
+ email: Optional[str] = None
21
+ request_message: Optional[str] = None
22
+
23
+ @classmethod
24
+ def from_xml(cls, xml_content: str) -> "LoginResponse":
25
+
26
+ root = ElementTree.fromstring(xml_content)
27
+
28
+ def get_value(xml_name: str) -> Optional[str]:
29
+ element = root.find(xml_name)
30
+
31
+ if element is None:
32
+ return None
33
+
34
+ return element.text
35
+
36
+ status_value = get_value("REQSTATUS")
37
+
38
+ request_status = None
39
+
40
+ if status_value:
41
+ try:
42
+ request_status = LDAPStatusEnum(status_value)
43
+ except ValueError:
44
+ request_status = None
45
+
46
+ return cls(
47
+ type=get_value("TYPE"),
48
+ app_name=get_value("APPLINAME"),
49
+ request_status=request_status,
50
+ date=get_value("DATE"),
51
+ cuid=get_value("CUID"),
52
+ full_name=get_value("FULLNAME"),
53
+ name=get_value("NAME"),
54
+ firstname=get_value("FIRSTNAME"),
55
+ description=get_value("DESCRIPTION"),
56
+ phone_number=get_value("PHONENUMBER"),
57
+ email=get_value("EMAIL"),
58
+ request_message=get_value("MESSAGE"),
59
+ )
@@ -0,0 +1,16 @@
1
+ from dataclasses import dataclass
2
+ from typing import Any, Dict, List
3
+
4
+
5
+ @dataclass
6
+ class SmsRequest:
7
+ sender: str
8
+ recipients: List[str]
9
+ message: str
10
+
11
+ def to_dict(self) -> Dict[str, Any]:
12
+ return {
13
+ "from": self.sender,
14
+ "to": self.recipients,
15
+ "message": self.message,
16
+ }
@@ -0,0 +1,3 @@
1
+ from . import ldap_status_enum
2
+
3
+ __all__ = ["ldap_status_enum"]
@@ -0,0 +1,6 @@
1
+ from enum import Enum
2
+
3
+
4
+ class LDAPStatusEnum(str, Enum):
5
+ SUCCESS = "SUCCESS"
6
+ ERROR = "ERROR"
@@ -0,0 +1,17 @@
1
+ from services.auth_service import AuthService
2
+
3
+ auth_service = AuthService(
4
+ ldap_base_url="http://10.25.2.25:8080/ldap",
5
+ notification_base_url="http://10.25.2.104:3010",
6
+ app_name="Testing Auth Module",
7
+ )
8
+
9
+ res = auth_service.authenticate(
10
+ cuid="wpkw8206",
11
+ password="Gbaigbai#2000",
12
+ message="OTP Test",
13
+ sms_sender_name="OrangeMoney",
14
+ email_sender_address="zmatondo.ext@orange.com",
15
+ )
16
+
17
+ print(res)
File without changes
@@ -0,0 +1,111 @@
1
+ """Services d'authentification et d'envoi de code de notification."""
2
+
3
+ from dtos import AuthResponse, EmailRequest, LoginRequest, SmsRequest
4
+ from clients.ldap_client import LdapClient
5
+ from clients.notification_client import NotificationClient
6
+ from enums.ldap_status_enum import LDAPStatusEnum
7
+
8
+
9
+ class AuthService:
10
+ """Service responsable de l'authentification des utilisateurs.
11
+
12
+ Le service authentifie un utilisateur auprès du serveur LDAP, puis
13
+ envoie un message par SMS ou par e-mail selon les coordonnées
14
+ disponibles dans la réponse LDAP.
15
+ """
16
+
17
+ def __init__(
18
+ self,
19
+ ldap_base_url: str,
20
+ notification_base_url: str,
21
+ app_name: str,
22
+ ):
23
+ """Initialise le service d'authentification.
24
+
25
+ Args:
26
+ ldap_base_url: URL de base du service LDAP.
27
+ notification_base_url: URL de base du service de notification.
28
+ app_name: Nom de l'application utilisé lors de l'authentification.
29
+ """
30
+ self.ldap_base_url = ldap_base_url
31
+ self.notification_base_url = notification_base_url
32
+ self.app_name = app_name
33
+
34
+ def authenticate(
35
+ self,
36
+ cuid: str,
37
+ password: str,
38
+ message: str,
39
+ sms_sender_name: str = "Orange",
40
+ email_sender_address: str = "no-reply@orange.com",
41
+ email_subject: str = "OTP",
42
+ ) -> AuthResponse:
43
+ """Authentifie un utilisateur et lui envoie un message.
44
+
45
+ L'authentification est effectuée auprès du serveur LDAP. Si elle
46
+ réussit, le message est envoyé par SMS lorsque le numéro de téléphone
47
+ est disponible. À défaut, il est envoyé par e-mail si une adresse
48
+ e-mail est disponible.
49
+
50
+ Args:
51
+ cuid: Identifiant de l'utilisateur.
52
+ password: Mot de passe de l'utilisateur.
53
+ message: Contenu du message à envoyer.
54
+ sms_sender_name: Nom de l'expéditeur du SMS.
55
+ email_sender_address: Adresse e-mail utilisée comme expéditeur.
56
+ email_subject: Objet de l'e-mail.
57
+
58
+ Returns:
59
+ Un objet ``AuthResponse`` contenant le CUID, l'adresse e-mail
60
+ et le numéro de téléphone associés à l'utilisateur authentifié.
61
+
62
+ Raises:
63
+ Exception: Si l'authentification LDAP échoue.
64
+ """
65
+ ldap_client = LdapClient(
66
+ base_url=self.ldap_base_url,
67
+ timeout=15,
68
+ )
69
+
70
+ login_request = LoginRequest(
71
+ app_name=self.app_name,
72
+ cuid=cuid,
73
+ password=password,
74
+ )
75
+
76
+ ldap_response = ldap_client.authenticate(login_request)
77
+
78
+ if ldap_response.request_status != LDAPStatusEnum.SUCCESS:
79
+ raise Exception("Cuid ou mot de passe incorrect.")
80
+
81
+ notification_client = NotificationClient(
82
+ base_url=self.notification_base_url,
83
+ timeout=15,
84
+ )
85
+
86
+ email = ldap_response.email
87
+ phone_number = ldap_response.phone_number
88
+
89
+ if phone_number is not None:
90
+ sms_request = SmsRequest(
91
+ sender=sms_sender_name,
92
+ recipients=[phone_number],
93
+ message=message,
94
+ )
95
+ notification_client.send_sms(sms_request)
96
+
97
+ elif email is not None:
98
+ email_request = EmailRequest(
99
+ sender=email_sender_address,
100
+ recipients=[email],
101
+ subject=email_subject,
102
+ text=message,
103
+ html=f"<h2>{message}</h2>",
104
+ )
105
+ notification_client.send_mail(email_request)
106
+
107
+ return AuthResponse(
108
+ email=email,
109
+ phone_number=phone_number,
110
+ cuid=cuid,
111
+ )