-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp.py
174 lines (161 loc) · 6.97 KB
/
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import csv
import random
from flask import Flask, make_response, request, Response, jsonify
from pydantic import BaseModel
from flask_pydantic import validate
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from typing import Tuple, Union
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
analyzer = SentimentIntensityAnalyzer()
class Query(BaseModel):
sentence:str
app = Flask(__name__)
def decipher(encrypted_password: str) -> str:
"""Decrypt the password using the private key"""
with open('private_key.pem', 'r') as f:
private_key = RSA.import_key(f.read())
cipher = PKCS1_OAEP.new(private_key)
return cipher.decrypt(bytes.fromhex(encrypted_password)).decode()
@app.route("/status")
def hello() -> Response:
"""Health monitoring endpoint"""
return make_response(jsonify(1), 200)
@app.route("/welcome/<string:name>")
def welcome(name: str) -> Response:
"""Welcome endpoint"""
return make_response(jsonify(f"Bonjour {name}"), 200)
@app.route("/public_key")
def public_key() -> Response:
"""Public key endpoint: returns the public key in PEM format,
in a JSON object with the key 'public_key'"""
try:
with open('public_key.pem', 'r') as f:
public_key = RSA.import_key(f.read())
data = {'public_key': public_key.export_key().decode()}
return make_response(jsonify(data), 200)
except FileNotFoundError:
return make_response(jsonify("No public key found"), 500)
def extract_header(header: dict) -> Union[Tuple[str, str], Response]:
"""Extract the username and password from the header.
Authentication is expected in the header {Authentication: username:password}.
Returns a tuple (username, password) if everything goes well, or a Response object
when there is an error"""
if 'Authentication' not in header:
return make_response(jsonify("Missing Authentication header"), 400)
else:
try:
username, password = decipher(header['Authentication']).split(':', maxsplit=1)
except ValueError:
return make_response(jsonify("Invalid Authentication header. Expected"
" Authentication: <username:password>"), 400)
return username, password
def authenticate(username: str, password: str) -> Union[Response, dict]:
"""Authenticate the user. Reads from the CSV file 'credentials.csv' and compares to username
and password.
Returns True if the user is authenticated, or a Response object when there is an error (invalid
user, invalid password, etc.))"""
try:
with open('credentials.csv', 'r') as f:
reader = csv.DictReader(f)
for line in reader:
if line['username'] == username:
if line['password'] == password:
return line
else:
return make_response(jsonify("Invalid password"), 403)
if username not in [row['username'] for row in reader]:
return make_response(jsonify("Invalid user"), 403)
except FileNotFoundError:
return make_response(jsonify("No credentials found"), 500)
# je pense qu'on peut autoriser les méthodes GET et POST pour /permissions
# l'authentification se fait avec le header Authentication, donc c'est possible
# avec les deux méthodes
@app.route("/permissions", methods=['POST'])
def permissions():
"""Permissions endpoint: returns the permissions of the user.
Authentication in expected in the header {Authentication: username=password}"""
header = dict(request.headers)
from_header = extract_header(header)
if type(from_header) == Response:
return from_header
else:
username, password = from_header
from_credentials = authenticate(username, password)
if type(from_credentials) == Response:
return from_credentials
else:
v1 = from_credentials['v1']
v2 = from_credentials['v2']
data = {'username': username, 'v1': int(v1), 'v2': int(v2)}
return make_response(jsonify(data), 200, data)
@app.route("/v1/sentiment", methods=['POST'])
@validate()
def sentiment_v1(body: Query):
"""Sentiment endpoint v1: returns the sentiment of the sentence in the
query parameters.
Authentication in expected in the header {Authentication: username=password}"""
header = dict(request.headers)
from_header = extract_header(header)
if type(from_header) == Response:
return from_header
else:
username, password = from_header
from_credentials = authenticate(username, password)
if type(from_credentials) == Response:
return from_credentials
else:
v1 = from_credentials['v1']
if v1 != '1':
return make_response(jsonify("User does not have permission v1"), 403)
if 'sentence' not in body.dict():
return make_response(jsonify("Missing sentence in body"), 400)
sentence = body.dict()['sentence']
score = random.uniform(-1, 1)
if score >= 0.05:
sentiment = 'positive'
elif score <= -0.05:
sentiment = 'negative'
else:
sentiment = 'neutral'
data = {'username': username,
'version': 'v1',
'sentence': sentence,
'sentiment': sentiment,
'score': score}
return make_response(jsonify(data), 200, data)
@app.route("/v2/sentiment", methods=['POST'])
@validate()
def sentiment_v2(body: Query):
"""Sentiment endpoint v2: returns the sentiment of the sentence in the
query parameters.
Authentication in expected in the header {Authentication: username=password}"""
header = dict(request.headers)
from_header = extract_header(header)
if type(from_header) == Response:
return from_header
else:
username, password = from_header
from_credentials = authenticate(username, password)
if type(from_credentials) == Response:
return from_credentials
else:
v2 = from_credentials['v2']
if v2 != '1':
return make_response(jsonify("User does not have permission v1"), 403)
if 'sentence' not in body.dict():
return make_response(jsonify("Missing sentence in body"), 400)
sentence = body.dict()['sentence']
score = analyzer.polarity_scores(sentence)['compound']
if score >= 0.05:
sentiment = 'positive'
elif score <= -0.05:
sentiment = 'negative'
else:
sentiment = 'neutral'
data = {'username': username,
'version': 'v2',
'sentence': sentence,
'sentiment': sentiment,
'score': score}
return make_response(jsonify(data), 200, data)