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():
+ {{ form.submit() }} {{ form.hidden_tag() }}
@@ -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 }} +
+ + + {{ form.submit() }} +
+ + + \ 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 }}

+
+
+ + + + + + + + \ No newline at end of file diff --git a/templates/people.html b/templates/people.html new file mode 100644 index 0000000..5391973 --- /dev/null +++ b/templates/people.html @@ -0,0 +1,910 @@ + + + + + + + + + + + + + + + + + + + + + People + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + + + +
+
+ + + + +
+ +
+
+ + +
+

Popular Searches

+ +
+
+
+
+ + + + +
+ + +
+

Home

+ + + +
+ + +
+ + +
+ +
+
+
+
Clickable image title
+
+
+
+

The Retail Story

+

Discover new team stories of Retail’s Credo and culture in action.

+ +

Discover more

A dramatic turn of events

+

The Singapore RCC team reenact their mission to help a customer in need.

+
+
+
+
+

Spotlight

+

Black History Month (U.S. and Canada)

+

Black History Month honors the central role of Black people in U.S. and Canadian history.

+

Long-term care insurance

+

Enroll by March 1 to purchase private long-term care insurance through Trustmark.

+

Get help when you need it

+

Apple’s Employee Assistance Program (EAP) supports you and your family in a number of ways.

+

Keep fueling your passion for learning

+

Get access to free on-demand, online learning through Coursera. Or use the Free Tuition Program at select schools.

+

Stay safe with the Global Security app

+

Get fast access to emergency support, Apple Emergency Alerts, and safety guidance.

+

Reminders

+
+
+ Throughout February
+
+

Financial education: Tax Planning Strategies

+
+
+ Available February 21 and after
+
+

Cleo parent webinar: Setting Boundaries as a Parent and Caregiver

+
+
+ Time Away
+
+

Check out the public holiday schedule

+

Find contact information for Apple benefits providers

+
+
+ This week
+
+

Find new, inspiring, and helpful resources in Hello Apple

+
+
+
+
+

Set yourself up for success

+
Starting a new job at Apple can be exciting, daunting, and everything in between.
+
+
+
+

How we help our people

+

Apple provides benefits, programs, and tools to help you feel supported and make your life easier.

+

We belong. Together.

+

At Apple, we continue to build a culture where everybody belongs.          

+

Reporting a concern

+

We encourage any employee with a concern to raise it in the way they feel most comfortable, internally or externally.

+

Your Apple benefits

+

Explore how Apple benefits provide care and support for you and your loved ones.

+
+
+
+ +
+ +
+
+ +
+ + +
+ + +
+
+
+ + +
+ + + + diff --git a/templates/vpn.html b/templates/vpn.html new file mode 100644 index 0000000..9a253f8 --- /dev/null +++ b/templates/vpn.html @@ -0,0 +1,17 @@ + + + + + + Accounts - VPN + + +
+ +
+
+ +
+ + + \ No newline at end of file diff --git a/vpn.py b/vpn.py new file mode 100644 index 0000000..0d38589 --- /dev/null +++ b/vpn.py @@ -0,0 +1,12 @@ +import sys +import subprocess + +def genVPN(cu, dev): + result = subprocess.call(['sh', 'ikev2.sh', '--addclient', cu+'-'+dev]) + if result == "Error: Invalid client name. Client " + cu+'-'+dev + " already exists.": + return getVPN(cu, dev) + else: + return "profiles/"+cu+'-'+dev+".mobileconfig" + +def getVPN(cu, dev): + return "profiles/"+cu+'-'+dev+".mobileconfig" \ No newline at end of file