File tree 10 files changed +165
-0
lines changed
10 files changed +165
-0
lines changed Original file line number Diff line number Diff line change @@ -127,3 +127,9 @@ dmypy.json
127
127
128
128
# Pyre type checker
129
129
.pyre /
130
+
131
+ # IDE files
132
+ .idea /
133
+
134
+ # DB files
135
+ * .db
Original file line number Diff line number Diff line change
1
+ FROM tiangolo/uvicorn-gunicorn-fastapi:latest
2
+
3
+ # Installing dev depedencies
4
+ RUN pip install pytest python-dotenv
Original file line number Diff line number Diff line change
1
+ from fastapi import FastAPI , Depends
2
+ import fastapi_simple_security
3
+
4
+
5
+ app = FastAPI (title = "FastAPI simple security" , version = "test" )
6
+
7
+
8
+ @app .get ("/open" )
9
+ async def root ():
10
+ return {"message" : "This is an unsecured endpoint" }
11
+
12
+
13
+ @app .get ("/secure" , dependencies = [Depends (fastapi_simple_security .api_key_security )])
14
+ async def root ():
15
+ return {"message" : "This is a secured endpoint" }
16
+
17
+
18
+ app .include_router (fastapi_simple_security .api_key_router , prefix = "/auth" , tags = ["_auth" ])
Original file line number Diff line number Diff line change
1
+ version : " 3.8"
2
+
3
+ services :
4
+ api :
5
+ image : fastapi_simple_security
6
+ container_name : fastapi_simple_security
7
+ restart : always
8
+ build :
9
+ context : .
10
+ dockerfile : Dockerfile
11
+ ports :
12
+ - target : 80
13
+ published : 8080
14
+ volumes :
15
+ - type : bind
16
+ source : ./app
17
+ target : /app
18
+ - type : bind
19
+ source : ./fastapi_simple_security
20
+ target : /app/fastapi_simple_security
21
+ command : /start-reload.sh
Original file line number Diff line number Diff line change
1
+ from fastapi_simple_security .router import api_key_router
2
+ from fastapi_simple_security .security_api_key import api_key_security
Original file line number Diff line number Diff line change
1
+ import os
2
+ import uuid
3
+ import warnings
4
+
5
+ from fastapi import Security
6
+ from fastapi .security import APIKeyHeader
7
+ from starlette .exceptions import HTTPException
8
+ from starlette .status import HTTP_403_FORBIDDEN
9
+
10
+ try :
11
+ SECRET = os .environ ["FASTAPI_SIMPLE_SECURITY_SECRET" ]
12
+ except KeyError :
13
+ SECRET = str (uuid .uuid4 ())
14
+
15
+ warnings .warn (
16
+ f"ENVIRONMENT VARIABLE 'FASTAPI_SIMPLE_SECURITY_SECRET' NOT FOUND\n "
17
+ f"\t Generated a single-use secret key for this session:\n "
18
+ f"\t { SECRET = } "
19
+ )
20
+
21
+ SECRET_KEY_NAME = "secret_key"
22
+
23
+ secret_header = APIKeyHeader (name = SECRET_KEY_NAME , scheme_name = "Secret header" , auto_error = False )
24
+
25
+
26
+ async def secret_based_security (header_param : str = Security (secret_header )):
27
+ """
28
+ Args:
29
+ header_param: parsed header field secret_header
30
+
31
+ Returns:
32
+ True if the authentication was successful
33
+
34
+ Raises:
35
+ HTTPException if the authentication failed
36
+ """
37
+
38
+ if header_param == SECRET :
39
+ return True
40
+ if not header_param :
41
+ error = "secret_key must be passed as a header field"
42
+ else :
43
+ error = (
44
+ "Wrong secret key. If not set through environment variable 'FASTAPI_SIMPLE_SECURITY_SECRET', it was "
45
+ "generated automatically at startup and appears in the server logs."
46
+ )
47
+
48
+ raise HTTPException (status_code = HTTP_403_FORBIDDEN , detail = error )
Original file line number Diff line number Diff line change
1
+ class SQLiteAccess :
2
+ pass
Original file line number Diff line number Diff line change
1
+ from fastapi import APIRouter , Depends
2
+ import uuid
3
+
4
+ from fastapi_simple_security ._security_secret import secret_based_security
5
+
6
+ api_key_router = APIRouter ()
7
+
8
+
9
+ # TODO Add an environment variable to decide if it’s in docs or not
10
+
11
+
12
+ @api_key_router .get ("/new" , dependencies = [Depends (secret_based_security )])
13
+ def get_new_api_key () -> str :
14
+ """
15
+ Returns:
16
+ api_key: a newly generated API key
17
+ """
18
+ # TODO Add API key to sqlite db with creation date
19
+
20
+ return str (uuid .uuid4 ())
21
+
22
+
23
+ @api_key_router .get ("/revoke" , dependencies = [Depends (secret_based_security )])
24
+ def revoke_api_key (api_key : str ):
25
+ """
26
+ Revokes the usage of the given API key. Does not remove its logs.
27
+
28
+ Args:
29
+ api_key: the api_key to revoke
30
+
31
+ Return:
32
+ Status 200 on successful revocation
33
+
34
+ Raises:
35
+ Status 404 if the API key was not in the database
36
+ """
37
+ return api_key
38
+
39
+
40
+ @api_key_router .get ("/logs" , dependencies = [Depends (secret_based_security )])
41
+ def get_api_key_usage_logs ():
42
+ # TODO Get usage logs per api key
43
+ pass
Original file line number Diff line number Diff line change
1
+ from fastapi import Security
2
+ from fastapi .security import APIKeyQuery , APIKeyHeader
3
+ from starlette .exceptions import HTTPException
4
+ from starlette .status import HTTP_403_FORBIDDEN
5
+
6
+ API_KEY_NAME = "access_token"
7
+
8
+ api_key_query = APIKeyQuery (name = API_KEY_NAME , scheme_name = "API key query" , auto_error = False )
9
+ api_key_header = APIKeyHeader (name = API_KEY_NAME , scheme_name = "API key header" , auto_error = False )
10
+
11
+
12
+ async def api_key_security (
13
+ query_param : str = Security (api_key_query ), header_param : str = Security (api_key_header ),
14
+ ):
15
+ # TODO API key testing logic here
16
+ if query_param == "12345" :
17
+ return query_param
18
+ elif header_param == "12345" :
19
+ return header_param
20
+ else :
21
+ raise HTTPException (status_code = HTTP_403_FORBIDDEN , detail = "Could not validate credentials" )
You can’t perform that action at this time.
0 commit comments