Compare commits
2 Commits
9a70ef5bb1
...
7f434069f4
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7f434069f4 | ||
![]() |
236cadf901 |
@@ -15,16 +15,20 @@ parser.add_argument('name')
|
||||
parser.add_argument('value')
|
||||
parser.add_argument('type')
|
||||
|
||||
# verander de variabelen hieronder als je de script handmatig uitvoert
|
||||
|
||||
dnsserver = os.environ.get('DNS_SERVER')
|
||||
dnsserverport = int(os.environ.get('DNS_PORT', default=53))
|
||||
debug = bool(os.environ.get('API_DEBUG', default=False))
|
||||
|
||||
# end change
|
||||
|
||||
if dnsserver is None:
|
||||
print('You did not set a DNS_SERVER environ')
|
||||
exit(1)
|
||||
|
||||
|
||||
def check_ipv4(ipv4):
|
||||
def check_ipv4(ipv4): # check if given ipv4 address in string is a valid one
|
||||
try:
|
||||
if type(ip_address(ipv4)) is not IPv4Address: # als ip adress ongelig is
|
||||
raise ValueError # raise value error
|
||||
@@ -34,13 +38,7 @@ def check_ipv4(ipv4):
|
||||
return True # adres correct
|
||||
|
||||
|
||||
def make_fqdn_check(subname, parentdomain):
|
||||
"""
|
||||
De functie moet de subdomain naam met de parent domain samenvoegen en daarna op errors controleren
|
||||
:param subname: hostname
|
||||
:param parentdomain: zone name
|
||||
:return: hostname.domain.tld in string
|
||||
"""
|
||||
def make_fqdn_check(subname, parentdomain): # make fqdn from given subdomain name, also check if valid
|
||||
fqdn = f'{subname}.{parentdomain}'
|
||||
if True: # do some regex checking if it made a correct fqdn, currently passing and trusting user input!!!
|
||||
return fqdn
|
||||
@@ -48,27 +46,27 @@ def make_fqdn_check(subname, parentdomain):
|
||||
return False
|
||||
|
||||
|
||||
def validate_authorization(req):
|
||||
def validate_authorization(req): # validate authorization header
|
||||
jwt = req.get('Authorization')
|
||||
if jwt is None:
|
||||
return False
|
||||
|
||||
jwt = jwt.split(' ')[1]
|
||||
jwt = jwt.split(' ')[1] # get jwt from header
|
||||
|
||||
if GoogleOID.check_jwt(jwt)['error'] is False:
|
||||
if GoogleOID.check_jwt(jwt)['error'] is False: # check if jwt is valid
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class domains(Resource):
|
||||
def get(self):
|
||||
def get(self): # list domains
|
||||
"""
|
||||
list all domains
|
||||
:return: {"domains": ["name":"a.b"], "error":false}
|
||||
"""
|
||||
# Vooralsnog moet je handmatig domains toevoegen aan BIND, dus dit heeft niet veel nut, hardcoded domeinen.
|
||||
if validate_authorization(request.headers) is False:
|
||||
if validate_authorization(request.headers) is False: # check authorization
|
||||
return {"error": True, "reason": "Invalid authorization header"}, 403
|
||||
|
||||
return {'domains': ['school.test'], 'error': False}
|
||||
@@ -81,7 +79,7 @@ class domain(Resource):
|
||||
:param fqdn: domain name
|
||||
:return:
|
||||
"""
|
||||
if validate_authorization(request.headers) is False:
|
||||
if validate_authorization(request.headers) is False: # check authorization
|
||||
return {"error": True, "reason": "Invalid authorization header"}, 403
|
||||
|
||||
args = parser.parse_args()
|
||||
@@ -94,21 +92,21 @@ class domain(Resource):
|
||||
if t != 'A':
|
||||
return {"error": True, "reason": "Only A records are supported"}, 400
|
||||
|
||||
fqdn = make_fqdn_check(n, dmn)
|
||||
fqdn = make_fqdn_check(n, dmn) # combine subdomain with parent domain
|
||||
if fqdn is False:
|
||||
return {"error": True, "reason": "Invalid subdomain, domain combination"}, 400
|
||||
|
||||
dns = DnsZone(dmn, dnsserver, 1, dnsserverport)
|
||||
dns = DnsZone(dmn, dnsserver, 1, dnsserverport) # setup dnszone
|
||||
|
||||
current = dns.check_address(fqdn)
|
||||
current = dns.check_address(fqdn) # check if address exists
|
||||
|
||||
if current['error'] is True:
|
||||
if 'not found' not in current['error_text']: # als er een error is wat niet een not found is
|
||||
if 'not found' not in current['error_text']: # als er een error is wat NIET een "not found" is
|
||||
return {"error": True, "reason": current['error_text']}, 500
|
||||
else: # record bestond al niet
|
||||
return {"error": True, "reason": "Record does not exist"}, 400
|
||||
|
||||
new = dns.clear_address(fqdn)
|
||||
new = dns.clear_address(fqdn) # delete record
|
||||
|
||||
if new['error'] is True: # als er een error is met het weghalen van dns regel
|
||||
return {"error": True, "reason": new['error_text']}, 500
|
||||
@@ -121,12 +119,11 @@ class domain(Resource):
|
||||
:param dmn: domain name
|
||||
:return: alle dns A records als keys
|
||||
"""
|
||||
# de dnszone library kan geen lijst met records opvragen, een andere manier uitzoeken? mongodb bijhouden?
|
||||
if validate_authorization(request.headers) is False:
|
||||
if validate_authorization(request.headers) is False: # check authorization
|
||||
return {"error": True, "reason": "Invalid authorization header"}, 403
|
||||
|
||||
dns = DnsZone(dmn, dnsserver, 1, dnsserverport)
|
||||
current = dns.list_addresses()
|
||||
dns = DnsZone(dmn, dnsserver, 1, dnsserverport) # setup dnszone
|
||||
current = dns.list_addresses() # get all a records
|
||||
|
||||
if current['error'] is True:
|
||||
return {"error": True, "reason": current['error_text']}, 500
|
||||
@@ -140,7 +137,7 @@ class domain(Resource):
|
||||
:param dmn: domain name
|
||||
:return:
|
||||
"""
|
||||
if validate_authorization(request.headers) is False:
|
||||
if validate_authorization(request.headers) is False: # check authorization
|
||||
return {"error": True, "reason": "Invalid authorization header"}, 403
|
||||
|
||||
args = parser.parse_args()
|
||||
@@ -153,7 +150,7 @@ class domain(Resource):
|
||||
if t != 'A': # als record type niet A is
|
||||
return {"error": True, "reason": "Only A records are supported"}, 400
|
||||
|
||||
if check_ipv4(v) is False:
|
||||
if check_ipv4(v) is False: # check if ip is valid
|
||||
return {"error": True, "reason": "Value is not correct"}, 400
|
||||
|
||||
fqdn = make_fqdn_check(n, dmn) # maak fqdn
|
||||
@@ -188,8 +185,9 @@ api.add_resource(domains, '/api/v1/dns/domains')
|
||||
api.add_resource(domain, '/api/v1/dns/domain/<dmn>')
|
||||
|
||||
if __name__ == '__main__':
|
||||
if check_ipv4(dnsserver) is False: # cant use dns in resolver so check if direct ip is given if not
|
||||
dnsserver = resolver.resolve(dnsserver, "A")[0].to_text() # set try to resolve given string
|
||||
if check_ipv4(dnsserver) is False: # cant use domain in resolver so check if direct ip is given if not
|
||||
dnsserver = resolver.resolve(dnsserver, "A")[0].to_text() # try to resolve given string
|
||||
|
||||
GoogleOID = GoogleOID()
|
||||
app.run(debug=debug, host='0.0.0.0', port=5001)
|
||||
# do not setup dns zone globally because it errors on simultaneous requests
|
||||
GoogleOID = GoogleOID() # setup google oid
|
||||
app.run(debug=debug, host='0.0.0.0', port=5001) # run werkzeug
|
||||
|
@@ -3,8 +3,9 @@ class GoogleOID:
|
||||
import requests
|
||||
import jwt
|
||||
import os
|
||||
client_secret = os.environ.get('OPENID_SECRET')
|
||||
if client_secret is None:
|
||||
client_secret = os.environ.get('OPENID_SECRET') # change this to your secret if running manually
|
||||
|
||||
if client_secret is None: # if environ not set
|
||||
print('No OPENID_SECRET environ')
|
||||
exit(1)
|
||||
|
||||
@@ -14,47 +15,42 @@ class GoogleOID:
|
||||
self.settings = {'client_id': '954325872153-1v466clrtgg6h4ptt2ne5pgpb9mhilr5.apps.googleusercontent.com',
|
||||
'client_secret': client_secret,
|
||||
'callback_uri': 'http://dns.mashallah.nl:5000/login/gcp/callback',
|
||||
'key_server': 'https://www.googleapis.com/oauth2/v3/certs'}
|
||||
'key_server': 'https://www.googleapis.com/oauth2/v3/certs'} # global oid settings
|
||||
|
||||
def settings(self):
|
||||
def settings(self): # make it so that the settings variable is callable
|
||||
return self.settings
|
||||
|
||||
def get_token_from_code(self, code):
|
||||
def get_token_from_code(self, code): # get usable api token from oauth code
|
||||
data = {'grant_type': 'authorization_code',
|
||||
'client_id': self.settings['client_id'],
|
||||
'client_secret': self.settings['client_secret'],
|
||||
'redirect_uri': self.settings['callback_uri'],
|
||||
'code': code}
|
||||
|
||||
r = self.requests.post('https://oauth2.googleapis.com/token', data=data)
|
||||
r = self.requests.post('https://oauth2.googleapis.com/token', data=data) # exchange code for token
|
||||
|
||||
if r.status_code != 200:
|
||||
if r.status_code != 200: # if not successful
|
||||
return {'error': True, 'reason': f'Could not exchange code for access key'}
|
||||
|
||||
return {'error': False, 'data': r.json()}
|
||||
|
||||
def get_profile_information(self, token):
|
||||
def get_profile_information(self, token): # get google profile with token
|
||||
headers = {'Authorization': f'Bearer {token}'}
|
||||
|
||||
r = self.requests.get('https://openidconnect.googleapis.com/v1/userinfo', headers=headers)
|
||||
|
||||
if r.status_code != 200:
|
||||
if r.status_code != 200: # if not successful
|
||||
return {'error': True, 'reason': 'Could not get profile info'}
|
||||
|
||||
return {'error': False, 'profile': r.json()}
|
||||
|
||||
def check_jwt(self, bearer):
|
||||
"""
|
||||
Decodes JWT and checks if it is for us from google
|
||||
:param bearer: JWT token in base64 format
|
||||
:return: {'error': False, 'data': decoded jwt dict}
|
||||
"""
|
||||
jwks_client = self.jwt.PyJWKClient(self.settings['key_server'])
|
||||
signing_key = jwks_client.get_signing_key_from_jwt(bearer).key
|
||||
def check_jwt(self, bearer): # check if jwt is signed by google and valid
|
||||
jwks_client = self.jwt.PyJWKClient(self.settings['key_server']) # setup jwks client with keyserver
|
||||
signing_key = jwks_client.get_signing_key_from_jwt(bearer).key # extract signing key from jwt
|
||||
|
||||
try:
|
||||
try: # fails if jwt is not valid eg. not signed by google, expired, wrong application
|
||||
decoded = self.jwt.decode(bearer, signing_key, algorithms=["RS256"], audience=self.settings['client_id'])
|
||||
except self.jwt.exceptions.DecodeError:
|
||||
except self.jwt.exceptions.DecodeError: # catch generic error
|
||||
return {'error': True, 'reason': 'Error decoding JWT'}
|
||||
|
||||
return {'error': False, 'data': decoded}
|
||||
|
BIN
eindopdracht/dns_gui/.assets/dashboard.png
Normal file
BIN
eindopdracht/dns_gui/.assets/dashboard.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
BIN
eindopdracht/dns_gui/.assets/login.png
Normal file
BIN
eindopdracht/dns_gui/.assets/login.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.4 KiB |
40
eindopdracht/dns_gui/README.md
Normal file
40
eindopdracht/dns_gui/README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# DNS Manager GUI
|
||||
In deze folder bevind zich alle code omtrent de web-gui voor de DNS manager.
|
||||
|
||||
## Install
|
||||
### Container
|
||||
Als je direct de container gebruikt moet je de environment variables gebruiken die op de docker page staat
|
||||
https://hub.docker.com/r/4grxfq/gui/ vergeet ook poort 5000 niet te openen naar je container
|
||||
|
||||
### Manual install
|
||||
1. verander de variabelen in gui.py die aangepast moeten worden of zet de environment variabelen in je shell
|
||||
2. run ```pip install -r /app/requirements.txt```
|
||||
3. start flask ```python3 gui.py``` de webserver luistert naar poort 5000
|
||||
|
||||
|
||||
## Screenshots
|
||||
### Index page
|
||||
Op de index page wordt er een inlog form getoont, ook worden hyperlinks getoont naar de dashboard voor als je al
|
||||
ingelogt bent, en een link naar de uitlog pad. De username en password textvakken doen niets en zijn puur cosmetisch,,
|
||||
je kunt alleen inloggen met de klop die je redirect naar de google OID login pagina.
|
||||

|
||||
|
||||
### Dashboard page
|
||||
De dashboard page laat een basale tabel zien met alle A records die op de nameserver ingesteld staan. Bovenin wordt je
|
||||
profiel getoont zoals gekregen van google en opgeslagen in de database.
|
||||
|
||||
Met de knop "Query JWT" kan je de JWT opvragen die google mee heeft gegeven met het inloggen, deze JWT kan je gebruiken om de API handmatig met curl aan te roepen en
|
||||
is verplicht.
|
||||
|
||||
Met de Add SUB form kan je een gebruiker authorizeren om in laten te loggen. Als een gebruikter niet mag inloggen wordt
|
||||
tijdens het inloggen een error getoont met een 21 cijferig code. Die code moet in de Google UUID box worden ingevoerd
|
||||
om de desbetreffende gebruiker authorizatie te geven om in te kunnen loggen.
|
||||
|
||||
De tabel met DNS records spreekt voorzich. Alle bestaande records worden weergeven en de adressen zijn aanpasbaar.
|
||||
Wanneer er een adres aangepast moet worden is dat makkelijk te doen door de waarde aan te wijzigen en Update aan te
|
||||
klikken. Evendeels is het verwijderen van een record ook mogelijk door op Delete te klikken.
|
||||
|
||||
Een nieuwe record kan worden toegevoegd door de subdomain aan te geven met de juiste IPv4 Waarde. Momenteel zijn alleen
|
||||
A records ondersteund. Klik op Add om de record toe te voegen.
|
||||
|
||||

|
@@ -5,15 +5,20 @@ from mango import Mango
|
||||
from openid import *
|
||||
import os
|
||||
|
||||
# the options below you must change if you want to use a different zone and or want to run the script manually
|
||||
|
||||
zone = 'school.test'
|
||||
dns_api = os.environ.get('DNS_API')
|
||||
nosql_url = os.environ.get('NOSQL_URL')
|
||||
nosql_user = os.environ.get('NOSQL_USER')
|
||||
nosql_pass = os.environ.get('NOSQL_PASS')
|
||||
nosql_port = int(os.environ.get('NOSQL_PORT', default=27017))
|
||||
debug = bool(os.environ.get('API_DEBUG', default=False))
|
||||
nosql_port = int(os.environ.get('NOSQL_PORT', default=27017)) # get NOSQL_PORT as int
|
||||
debug = bool(os.environ.get('API_DEBUG', default=False)) # debug option as bool
|
||||
|
||||
# end editing
|
||||
|
||||
if dns_api is None or nosql_pass is None or nosql_port is None or nosql_user is None or nosql_url is None:
|
||||
# check if vars are not set
|
||||
print('Missing DNS or NOSQL environs')
|
||||
exit(1)
|
||||
|
||||
@@ -21,14 +26,14 @@ app = Flask(__name__)
|
||||
app.secret_key = secrets.token_hex(22)
|
||||
|
||||
|
||||
def record_update(name, typ, value, jwt):
|
||||
def record_update(name, typ, value, jwt): # update record via api server
|
||||
data = {'name': name, 'type': typ, 'value': value}
|
||||
headers = {'Authorization': f'Bearer {jwt}'}
|
||||
|
||||
r = requests.post(f'{dns_api}/api/v1/dns/domain/{zone}', data=data, headers=headers)
|
||||
|
||||
if r.status_code != 200:
|
||||
return {'error': True, 'reason': f'Request got status code: {r.status_code}'}
|
||||
if r.status_code != 200: # if not sucessful
|
||||
return {'error': True, 'reason': f'Request got status code: {r.status_code}'} # return status code from api
|
||||
|
||||
return {'error': False}
|
||||
|
||||
@@ -37,9 +42,10 @@ def records_get(jwt):
|
||||
headers = {'Authorization': f'Bearer {jwt}'}
|
||||
|
||||
r = requests.get(f'{dns_api}/api/v1/dns/domain/{zone}', headers=headers)
|
||||
if r.status_code != 200:
|
||||
return {'error': True, 'reason': f'Request got status code: {r.status_code}'}
|
||||
return r.json()['records']
|
||||
|
||||
if r.status_code != 200: # if not sucessful
|
||||
return {'error': True, 'reason': f'Request got status code: {r.status_code}'} # return status code from api
|
||||
return r.json()['records'] # return all the records as a dict
|
||||
|
||||
|
||||
def record_delete(name, jwt):
|
||||
@@ -47,18 +53,20 @@ def record_delete(name, jwt):
|
||||
headers = {'Authorization': f'Bearer {jwt}'}
|
||||
|
||||
r = requests.delete(f'{dns_api}/api/v1/dns/domain/{zone}', data=data, headers=headers)
|
||||
if r.status_code != 200:
|
||||
return {'error': True, 'reason': f'Request got status code: {r.status_code}'}
|
||||
|
||||
if r.status_code != 200: # if not sucessful
|
||||
return {'error': True, 'reason': f'Request got status code: {r.status_code}'} # return status code from api
|
||||
|
||||
return {'error': False}
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
def index(): # base index page
|
||||
return render_template('index.html')
|
||||
|
||||
|
||||
@app.route('/login/gcp/start')
|
||||
def login_start():
|
||||
def login_start(): # client gets redirected to google login page with parameters
|
||||
nonce = secrets.token_hex(16)
|
||||
return redirect(f'https://accounts.google.com/o/oauth2/v2/auth?'
|
||||
f'response_type=code'
|
||||
@@ -69,100 +77,90 @@ def login_start():
|
||||
|
||||
|
||||
@app.route('/login/gcp/callback')
|
||||
def login_callback():
|
||||
"""
|
||||
We get parameters:(code)
|
||||
:return:
|
||||
"""
|
||||
code = request.args.get('code')
|
||||
def login_callback(): # client gets returned from google with parameters
|
||||
code = request.args.get('code') # get the code given by google
|
||||
|
||||
if code is None:
|
||||
return 'Did not get correct parameters. Missing code'
|
||||
|
||||
grant = GoogleOID.get_token_from_code(code)
|
||||
grant = GoogleOID.get_token_from_code(code) # exchange code for access token
|
||||
|
||||
if grant['error'] is True:
|
||||
return f"Could not exchange code for token: {grant['reason']}"
|
||||
|
||||
profile = GoogleOID.get_profile_information(grant['data']['access_token'])
|
||||
profile = GoogleOID.get_profile_information(grant['data']['access_token']) # get profile info from google
|
||||
|
||||
if profile['error'] is True:
|
||||
return f"Could not get profile information: {profile['reason']}"
|
||||
|
||||
if db.google_check_sso_uuid(profile['profile']['sub'])['error'] is True:
|
||||
if db.google_check_sso_uuid(profile['profile']['sub'])['error'] is True: # check if user may login
|
||||
return f"Account is unavailable for login {profile['profile']['sub']}"
|
||||
|
||||
db.google_update_profile(profile['profile'])
|
||||
db.google_update_lastlogin(profile['profile']['sub'])
|
||||
db.google_overwrite_jwt(profile['profile']['sub'], grant['data']['id_token'])
|
||||
db.google_update_profile(profile['profile']) # update google profile in db
|
||||
db.google_update_lastlogin(profile['profile']['sub']) # change login date in db
|
||||
db.google_overwrite_jwt(profile['profile']['sub'], grant['data']['id_token']) # overwrite jwt in db
|
||||
|
||||
session['username'] = profile['profile']['sub']
|
||||
session['username'] = profile['profile']['sub'] # set flask session so user gets logged in
|
||||
|
||||
return redirect(url_for('dashboard'))
|
||||
return redirect(url_for('dashboard')) # redirect to dashboard
|
||||
|
||||
|
||||
@app.route('/dashboard', methods=['GET', 'POST'])
|
||||
def dashboard():
|
||||
if 'username' not in session:
|
||||
def dashboard(): # dns manager dashboard
|
||||
if 'username' not in session: # check if flask session is set(user logged in)
|
||||
return 'You are not logged in <a href="/">login</a>'
|
||||
|
||||
if request.method == 'POST':
|
||||
if request.method == 'POST': # if form posted
|
||||
na = request.form.get('name')
|
||||
ty = request.form.get('type')
|
||||
va = request.form.get('value')
|
||||
rq = request.form.get('request')
|
||||
|
||||
if rq != "Add" and rq != "Delete" and rq != "Update" and rq != "Query JWT" and rq != "Add SUB":
|
||||
# data is missing
|
||||
return 'Invalid request, did you use the dashboard?'
|
||||
|
||||
if rq == 'Add' or rq == 'Update':
|
||||
jwt = db.google_get_jwt(session['username'])
|
||||
if rq == 'Add' or rq == 'Update': # if user adds or updates a record
|
||||
jwt = db.google_get_jwt(session['username']) # get jwt from db
|
||||
|
||||
response = record_update(name=na, typ=ty, value=va, jwt=jwt)
|
||||
response = record_update(name=na, typ=ty, value=va, jwt=jwt) # update record
|
||||
|
||||
if response['error'] is True:
|
||||
if response['error'] is True: # if error with update record
|
||||
return f"Error processing request: {response['reason']}"
|
||||
|
||||
elif rq == 'Delete':
|
||||
jwt = db.google_get_jwt(session['username'])
|
||||
jwt = db.google_get_jwt(session['username']) # get jwt from db
|
||||
|
||||
response = record_delete(name=na, jwt=jwt)
|
||||
response = record_delete(name=na, jwt=jwt) # delete record
|
||||
|
||||
if response['error'] is True:
|
||||
if response['error'] is True: # if error with delete record
|
||||
return f"Error processing request: {response['reason']}"
|
||||
|
||||
elif rq == "Query JWT":
|
||||
jwt = db.google_get_jwt(session['username'])
|
||||
flash(jwt)
|
||||
elif rq == "Query JWT": # if user requests jwt from db
|
||||
jwt = db.google_get_jwt(session['username']) # get jwt from db
|
||||
flash(jwt) # flash jwt message
|
||||
|
||||
elif rq == "Add SUB":
|
||||
db.google_add_new_sub(va)
|
||||
elif rq == "Add SUB": # user adds a sub
|
||||
db.google_add_new_sub(va) # add sub to db
|
||||
|
||||
uuid = session['username']
|
||||
profile = db.google_get_profile(uuid)
|
||||
lastlogin = db.google_get_lastlogin(uuid)
|
||||
jwt = db.google_get_jwt(uuid)
|
||||
records = records_get(jwt)
|
||||
# the functions below are always returned even if POST is used this will keep the page the same after a POST request
|
||||
|
||||
uuid = session['username'] # get uuid from session
|
||||
profile = db.google_get_profile(uuid) # get profile from db
|
||||
lastlogin = db.google_get_lastlogin(uuid) # get lastlogin from db
|
||||
jwt = db.google_get_jwt(uuid) # get jwt from db
|
||||
records = records_get(jwt) # request records from dns api
|
||||
|
||||
return render_template('dashboard.html', profile=profile, records=records, lastlogin=lastlogin)
|
||||
|
||||
|
||||
@app.route('/logout')
|
||||
def logout():
|
||||
session.pop('username', None)
|
||||
def logout(): # user logs out
|
||||
session.pop('username', None) # remove flask session
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
@app.route('/login/gcp/addsub', methods=['POST'])
|
||||
def addsub():
|
||||
if 'username' not in session:
|
||||
return 'You are not logged in <a href="/">login</a>'
|
||||
|
||||
db.google_add_new_sub(request.form.get('sub'))
|
||||
return 'sub toegevoegd, gebruiker mag volgende keer inloggen'
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
db = Mango(nosql_user, nosql_pass, nosql_url, nosql_port)
|
||||
GoogleOID = GoogleOID()
|
||||
app.run(debug=debug, host='0.0.0.0')
|
||||
db = Mango(nosql_user, nosql_pass, nosql_url, nosql_port) # setup mango
|
||||
GoogleOID = GoogleOID() # initialize googleoid
|
||||
app.run(debug=debug, host='0.0.0.0') # run werkzeug
|
||||
|
@@ -10,15 +10,15 @@ class Mango:
|
||||
except ConnectionError:
|
||||
print('MongoDB connection error')
|
||||
|
||||
def google_check_sso_uuid(self, uuid):
|
||||
def google_check_sso_uuid(self, uuid): # checks if uuid exist in any document
|
||||
found = self.users.find_one({"sso.google.profile.sub": uuid})
|
||||
|
||||
if found is None:
|
||||
if found is None: # None if nothing found
|
||||
return {"error": True, "reason": "User not found"}
|
||||
else:
|
||||
return {"error": False}
|
||||
|
||||
def google_update_lastlogin(self, uuid):
|
||||
def google_update_lastlogin(self, uuid): # replaces lastlogin with current time for given user
|
||||
found = self.users.find_one({"sso.google.profile.sub": uuid})
|
||||
|
||||
if found is None:
|
||||
@@ -30,7 +30,7 @@ class Mango:
|
||||
|
||||
return {"error": False}
|
||||
|
||||
def google_update_profile(self, profile):
|
||||
def google_update_profile(self, profile): # overwrites user profile with the one that google has given
|
||||
found = self.users.find_one({"sso.google.profile.sub": profile['sub']})
|
||||
|
||||
found['sso']['google']['profile'] = profile
|
||||
@@ -39,23 +39,23 @@ class Mango:
|
||||
|
||||
return {"error": False}
|
||||
|
||||
def google_get_profile(self, uuid):
|
||||
def google_get_profile(self, uuid): # returns google profile as stored in db for a given user
|
||||
found = self.users.find_one(({"sso.google.profile.sub": uuid}))
|
||||
return found['sso']['google']['profile']
|
||||
|
||||
def google_get_lastlogin(self, uuid):
|
||||
def google_get_lastlogin(self, uuid): # returns lastlogin in pretty format for given user
|
||||
found = self.users.find_one(({"sso.google.profile.sub": uuid}))
|
||||
return found['sso']['google']['lastlogin'].strftime('%A %d-%m-%Y, %H:%M:%S')
|
||||
|
||||
def google_add_new_sub(self, uuid):
|
||||
def google_add_new_sub(self, uuid): # adds new document in db with only the sub for a given uuid
|
||||
self.users.insert_one({'sso':{'google':{'profile':{"sub": str(uuid)}}}})
|
||||
|
||||
def google_overwrite_jwt(self, uuid, jwt):
|
||||
def google_overwrite_jwt(self, uuid, jwt): # overwrite jwt in db for given user
|
||||
found = self.users.find_one({"sso.google.profile.sub": uuid})
|
||||
|
||||
found['sso']['google']['jwt'] = jwt
|
||||
self.users.replace_one({"sso.google.profile.sub": uuid}, found)
|
||||
|
||||
def google_get_jwt(self, uuid):
|
||||
def google_get_jwt(self, uuid): # return jwt as stored in db for given user
|
||||
found = self.users.find_one({"sso.google.profile.sub": uuid})
|
||||
return found['sso']['google']['jwt']
|
||||
|
@@ -3,8 +3,9 @@ class GoogleOID:
|
||||
import requests
|
||||
import jwt
|
||||
import os
|
||||
client_secret = os.environ.get('OPENID_SECRET')
|
||||
if client_secret is None:
|
||||
client_secret = os.environ.get('OPENID_SECRET') # change this to your secret if running manually
|
||||
|
||||
if client_secret is None: # if environ not set
|
||||
print('No OPENID_SECRET environ')
|
||||
exit(1)
|
||||
|
||||
@@ -14,47 +15,42 @@ class GoogleOID:
|
||||
self.settings = {'client_id': '954325872153-1v466clrtgg6h4ptt2ne5pgpb9mhilr5.apps.googleusercontent.com',
|
||||
'client_secret': client_secret,
|
||||
'callback_uri': 'http://dns.mashallah.nl:5000/login/gcp/callback',
|
||||
'key_server': 'https://www.googleapis.com/oauth2/v3/certs'}
|
||||
'key_server': 'https://www.googleapis.com/oauth2/v3/certs'} # global oid settings
|
||||
|
||||
def settings(self):
|
||||
def settings(self): # make it so that the settings variable is callable
|
||||
return self.settings
|
||||
|
||||
def get_token_from_code(self, code):
|
||||
def get_token_from_code(self, code): # get usable api token from oauth code
|
||||
data = {'grant_type': 'authorization_code',
|
||||
'client_id': self.settings['client_id'],
|
||||
'client_secret': self.settings['client_secret'],
|
||||
'redirect_uri': self.settings['callback_uri'],
|
||||
'code': code}
|
||||
|
||||
r = self.requests.post('https://oauth2.googleapis.com/token', data=data)
|
||||
r = self.requests.post('https://oauth2.googleapis.com/token', data=data) # exchange code for token
|
||||
|
||||
if r.status_code != 200:
|
||||
if r.status_code != 200: # if not successful
|
||||
return {'error': True, 'reason': f'Could not exchange code for access key'}
|
||||
|
||||
return {'error': False, 'data': r.json()}
|
||||
|
||||
def get_profile_information(self, token):
|
||||
def get_profile_information(self, token): # get google profile with token
|
||||
headers = {'Authorization': f'Bearer {token}'}
|
||||
|
||||
r = self.requests.get('https://openidconnect.googleapis.com/v1/userinfo', headers=headers)
|
||||
|
||||
if r.status_code != 200:
|
||||
if r.status_code != 200: # if not successful
|
||||
return {'error': True, 'reason': 'Could not get profile info'}
|
||||
|
||||
return {'error': False, 'profile': r.json()}
|
||||
|
||||
def check_jwt(self, bearer):
|
||||
"""
|
||||
Decodes JWT and checks if it is for us from google
|
||||
:param bearer: JWT token in base64 format
|
||||
:return: {'error': False, 'data': decoded jwt dict}
|
||||
"""
|
||||
jwks_client = self.jwt.PyJWKClient(self.settings['key_server'])
|
||||
signing_key = jwks_client.get_signing_key_from_jwt(bearer).key
|
||||
def check_jwt(self, bearer): # check if jwt is signed by google and valid
|
||||
jwks_client = self.jwt.PyJWKClient(self.settings['key_server']) # setup jwks client with keyserver
|
||||
signing_key = jwks_client.get_signing_key_from_jwt(bearer).key # extract signing key from jwt
|
||||
|
||||
try:
|
||||
try: # fails if jwt is not valid eg. not signed by google, expired, wrong application
|
||||
decoded = self.jwt.decode(bearer, signing_key, algorithms=["RS256"], audience=self.settings['client_id'])
|
||||
except self.jwt.exceptions.DecodeError:
|
||||
except self.jwt.exceptions.DecodeError: # catch generic error
|
||||
return {'error': True, 'reason': 'Error decoding JWT'}
|
||||
|
||||
return {'error': False, 'data': decoded}
|
||||
|
Reference in New Issue
Block a user