diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..3f007a0
Binary files /dev/null and b/.DS_Store differ
diff --git a/__pycache__/_forms.cpython-312.pyc b/__pycache__/_forms.cpython-312.pyc
new file mode 100644
index 0000000..c03e4e2
Binary files /dev/null and b/__pycache__/_forms.cpython-312.pyc differ
diff --git a/__pycache__/ad.cpython-312.pyc b/__pycache__/ad.cpython-312.pyc
index ec1aa82..00627e4 100644
Binary files a/__pycache__/ad.cpython-312.pyc and b/__pycache__/ad.cpython-312.pyc differ
diff --git a/__pycache__/vpn.cpython-312.pyc b/__pycache__/vpn.cpython-312.pyc
new file mode 100644
index 0000000..d98b586
Binary files /dev/null and b/__pycache__/vpn.cpython-312.pyc differ
diff --git a/__pycache__/yamlcon.cpython-312.pyc b/__pycache__/yamlcon.cpython-312.pyc
index 724f3db..12ae38f 100644
Binary files a/__pycache__/yamlcon.cpython-312.pyc and b/__pycache__/yamlcon.cpython-312.pyc differ
diff --git a/_forms.py b/_forms.py
new file mode 100644
index 0000000..03aaa65
--- /dev/null
+++ b/_forms.py
@@ -0,0 +1,8 @@
+import wtforms
+from ad import updatePassword
+
+class ChangePasswordForm(wtforms.Form):
+ # oldpw = wtforms.PasswordField('OldPassword', validators=[wtforms.validators.DataRequired()])
+ newpw = wtforms.PasswordField('NewPassword', validators=[wtforms.validators.DataRequired(), wtforms.validators.EqualTo('conpw', message='Passwords must match')])
+ conpw = wtforms.PasswordField('ConPassword')
+ submit = wtforms.SubmitField('Submit')
\ No newline at end of file
diff --git a/accounts.py b/accounts.py
index fc1a84d..daf9947 100644
--- a/accounts.py
+++ b/accounts.py
@@ -1,12 +1,17 @@
from flask import Flask, url_for
from flask_ldap3_login import LDAP3LoginManager
-from flask_login import LoginManager, login_user, UserMixin, current_user
-from flask import render_template_string, redirect, render_template
+from flask_login import LoginManager, login_user, UserMixin, current_user, logout_user
+from flask import render_template_string, redirect, render_template, request, send_file
from flask_ldap3_login.forms import LDAPLoginForm
-from mfa import generateOTP, generateSecret
+from mfa import generateOTP, generateSecret, generateProvisioningUri
# from ad import updateMfaSecret
from db import setupMfaSecret, updateMfaSecret, getMfaSecret, removeMfa
import yamlcon
+from wtforms import StringField
+from wtforms.validators import DataRequired
+import _forms
+from ad import updatePassword
+from vpn import genVPN, getVPN
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret'
@@ -64,7 +69,9 @@ class User(UserMixin):
def get_id(self):
return self.dn
-
+
+class LDAPLoginForm0(LDAPLoginForm):
+ mfacode = StringField('MFA')
# Declare a User Loader for Flask-Login.
# Simply returns the User if it exists in our 'database', otherwise
@@ -93,6 +100,11 @@ def home():
# Redirect users who are not logged in.
if not current_user or current_user.is_anonymous:
return redirect(url_for('login'))
+
+ if getMfaSecret(user=current_user.data['sAMAccountName']):
+ mfaStatus = "MFA is active"
+ else:
+ mfaStatus = "MFA is not active"
# User is logged in, so show them a page with their cn and dn.
# template = """
@@ -101,12 +113,13 @@ def home():
#
Email: {{ current_user.data.mail }}
# {{ current_user.dn }}
# """
- print(current_user)
- print(current_user.data)
- print(current_user.data['objectSid'])
+ # print(current_user)
+ # print(current_user.data)
+ # print(current_user.data['objectSid'])
# return render_template_string(template)
- return render_template('home.html', current_user=current_user, mfaurl=url_for('two_factor'))
+ return render_template('home.html', current_user=current_user, mfaurl=url_for('two_factor'), \
+ mfastatus=mfaStatus, logouturl=url_for('logout'), changepwurl=url_for('changepw'), vpnurl=url_for('vpn'))
@app.route('/2fa')
def two_factor():
@@ -121,10 +134,11 @@ def two_factor():
setupMfaSecret(user=current_user.data['sAMAccountName'], secret=generateSecret())
# currSecret = current_user.data['mfaSecret']
currSecret = getMfaSecret(user=current_user.data['sAMAccountName'])
+ uri = generateProvisioningUri(currSecret=currSecret, cu=current_user.data['sAMAccountName'])
code = ''
print(currSecret)
- return render_template('2fa.html', currSecret=currSecret, code=code, homeurl=url_for('home'), delurl=url_for('delTwoFactor'))
+ return render_template('2fa.html', currSecret=currSecret, uri=uri, code=code, homeurl=url_for('home'), delurl=url_for('delTwoFactor'))
@app.route('/del2fa')
def delTwoFactor():
@@ -152,6 +166,7 @@ def login():
@@ -159,16 +174,56 @@ def login():
# Instantiate a LDAPLoginForm which has a validator to check if the user
# exists in LDAP.
- form = LDAPLoginForm()
+ form = LDAPLoginForm0()
if form.validate_on_submit():
# Successfully logged in, We can now access the saved user object
# via form.user.
- login_user(form.user) # Tell flask-login to log them in.
- return redirect('/') # Send them home
-
+ login_user(form.user) # Tell flask-login to log them in.
+ if getMfaSecret(user=current_user.data['sAMAccountName']):
+ if form.mfacode.data == generateOTP(getMfaSecret(user=current_user.data['sAMAccountName'])):
+ return redirect('/') # Send them home
+ else:
+ logout_user()
+ form.mfacode.errors.append("Invalid MFA Code")
+ return redirect('/')
+ elif not getMfaSecret(user=current_user.data['sAMAccountName']):
+ login_user(form.user) # Tell flask-login to log them in.
+ return redirect('/')
return render_template_string(template, form=form)
+@app.route('/changepw', methods=['GET', 'POST'])
+def changepw():
+ if not current_user or current_user.is_anonymous:
+ return redirect(url_for('login'))
+
+ form = _forms.ChangePasswordForm(request.form)
+ if request.method == 'POST' and form.validate():
+ new_password = form.newpw.data
+ if updatePassword(cu=current_user.data['sAMAccountName'], newpw=new_password, adinfo=adinfo):
+ print('Password changed successfully!')
+ # flash('Password changed successfully!', 'success')
+ return redirect(url_for('home'))
+ else:
+ print('Failed to change password. Please try again.')
+ # flash('Failed to change password. Please try again.', 'danger')
+ return render_template('changepw.html', form=form)
+
+@app.route('/vpn', methods=['GET', 'POST'])
+def vpn():
+ if not current_user or current_user.is_anonymous:
+ return redirect(url_for('login'))
+
+ if request.args.get('dev'):
+ return send_file(genVPN(cu=current_user.data['sAMAccountName'], dev=request.args.get('dev')), as_attachment=True)
+
+ return render_template('vpn.html')
+
+@app.route('/logout')
+def logout():
+ logout_user()
+ return redirect('/')
+
if __name__ == '__main__':
- app.run()
\ No newline at end of file
+ app.run(host='0.0.0.0', port=81)
\ No newline at end of file
diff --git a/ad.py b/ad.py
index 991610d..b262bd3 100644
--- a/ad.py
+++ b/ad.py
@@ -1,8 +1,18 @@
from ms_active_directory import ADDomain
+import logging
+import sys
+
+logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
def updateMfaSecret(user, secret):
domain = ADDomain('corp.bbrunson.com')
session = domain.create_session_as_user('administrator@bbrunson.com', 'Mariposa2502$$$$')
-
success = session.overwrite_attribute_for_user(user, 'mfaSecret',
- secret)
\ No newline at end of file
+ secret)
+
+def updatePassword(cu, newpw, adinfo):
+ domain = ADDomain('corp.bbrunson.com')
+ session = domain.create_session_as_user(user=adinfo.get('adbind_user', ''), password=adinfo.get('adbind_pass', ''))
+ return session.reset_password_for_account(account=(session.find_user_by_sam_name(cu)), new_password=newpw)
+
+# updatePassword(current_user='brandon', newpw='Mariposa2502$$', oldpw='Mariposa2502$')
\ No newline at end of file
diff --git a/mfa.py b/mfa.py
index 9d03d95..7ae5893 100644
--- a/mfa.py
+++ b/mfa.py
@@ -11,4 +11,7 @@ def generateOTP(secret):
# return totp.verify(code)
def generateSecret():
- return pyotp.random_base32()
\ No newline at end of file
+ return pyotp.random_base32()
+
+def generateProvisioningUri(currSecret, cu):
+ return pyotp.totp.TOTP(currSecret).provisioning_uri(name=cu, issuer_name='Bbrunson Services')
\ No newline at end of file
diff --git a/templates/.DS_Store b/templates/.DS_Store
new file mode 100644
index 0000000..6e0e9be
Binary files /dev/null and b/templates/.DS_Store differ
diff --git a/templates/2fa.html b/templates/2fa.html
index d4454da..5f39ea7 100644
--- a/templates/2fa.html
+++ b/templates/2fa.html
@@ -8,6 +8,9 @@
{{ currSecret }}
{{ code }}
+
+
+
diff --git a/templates/changepw.html b/templates/changepw.html
new file mode 100644
index 0000000..fcf91ea
--- /dev/null
+++ b/templates/changepw.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Accounts - Change PW
+
+
+ {{ get_flashed_messages() }}
+ {{ form.errors }}
+
+
+
+
\ No newline at end of file
diff --git a/templates/home.html b/templates/home.html
index 000b2b0..ab2929d 100644
--- a/templates/home.html
+++ b/templates/home.html
@@ -9,11 +9,40 @@
Welcome, {{ current_user.data.givenName }}
Email: {{ current_user.data.mail }}
{{ current_user.dn }}
+ MFA status:
-
+ {{ mfastatus }}
-
+
+
+
+
MFA Status:
+
{{ mfastatus }}
+
+
+
+
+
+
+
+
+
+