Running powershell command in flask wtf form

hi all,

ive made this flask-wtf form

from flask import Flask, render_template, request
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, DecimalField, RadioField, SelectField, TextAreaField, FileField, SubmitField
from wtforms.validators import InputRequired, Length, DataRequired, EqualTo, Regexp, ValidationError
import re
import subprocess

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secretkey'


class PasswordForm(FlaskForm):
    un = StringField('Username', [InputRequired(message='please enter your Username')])
    op = PasswordField('Current Password', [InputRequired(message='please enter your current password')])
    np = PasswordField('New Password', [InputRequired(message='please enter your new password')])
    cnp = PasswordField('Confirm New Password')
    dom = SelectField('Domain', choices=[('lon-prod-dc01.domain.com', 'prod'), ('lon-corp-dc01.domain.com', 'corp'), ('dc01.robo84.net', 'robo84')])
    
    def validate_un(form, field):
        if not field.data == form.un.data.lower():
            raise ValidationError('Username needs to be Lowercase')
    
    def validate_np(form, field):
        if form.un.data:
            if any (name in field.data.lower() for name in form.un.data.split(".")):
                raise ValidationError('New password cant contain firstname or lastname')
        if field.data.lower() == form.op.data.lower():
            raise ValidationError('New password cant match Current password')
        if len(field.data) < 12:
            raise ValidationError('New password must be at least 12 characters')
        if not re.search(r"[0-9]", field.data):
            raise ValidationError('New password has to contain one number')
        if not re.search(r"[a-z]", field.data):
            raise ValidationError('New password has to contain one lower case character')
        if not re.search(r"[A-Z]", field.data):
            raise ValidationError('New password has to contain one upper case character')
        if not re.search(r"[\`\¬\!\"\£\$\%\^\&\*\(\)\-\_\=\+\\\|\[\]\;\'\#\,\.\/\{\}\:\@\~\<\>\?]", field.data):
            raise ValidationError('New password has to contain one special character')
        if not field.data == form.cnp.data:
            raise ValidationError('New password has to match Confirm new password')
        
    
@app.route('/password', methods=['GET', 'POST'])
def password():
    form = PasswordForm()
    if request.method == 'POST' and form.validate():
        return '<h1>The username is {}. The old password is {}. the new password is {}. changing for domain {}'.format(form.un.data, form.op.data, form.cnp.data, form.dom.data)
        return subprocess.run('C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe ipconfig', shell=True)
    return render_template('password.html', form=form)

if __name__ == '__main__':
    app.run(debug=True)

basically if all validation pass, when they click the submit button, it will return there values for username/old password/new password and domain

this works but now i want it to also run a PS command as well ie if all validations pass and they click on the submit button

for testing im just going to run an “ipconfig” in PS but im going to change this with the “set-adaccountpassword” with the variables they entered so it changes there password for them

thanks,
rob

tried this but didnt work,

@app.route('/password', methods=['GET', 'POST'])
def password():
    form = PasswordForm()
    if request.method == 'POST' and form.validate():
        return '<h1>The username is {}. The old password is {}. the new password is {}. changing for domain {}'.format(form.un.data, form.op.data, form.cnp.data, form.dom.data)
        subprocess.run('C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe Set-ADAccountPassword -Identity form.un.data -OldPassword (ConvertTo-SecureString -AsPlainText "form.op.data" -Force) -NewPassword (ConvertTo-SecureString -AsPlainText "form.cnp.data" -Force -server form.dom.data)', shell=True)
    return render_template('password.html', form=form)

if __name__ == '__main__':
    app.run(debug=True)

to test it, i log out and back in with the new password that i have set but the login is still using my old password

ok done a test and this works

import subprocess

un = "robert.wild"
op = "Password01!"
cnp = "Password2024!"
dom = "prod"

subprocess.run(f'powershell.exe write-host {un} {op} {cnp} {dom}', shell=True)

but when i put it in my script ie inbetween the lines of

return '<h1>The username is {}. The old password is {}. the new password is {}. changing for domain {}'.format(form.un.data, form.op.data, form.cnp.data, form.dom.data)
 subprocess.run(f'powershell.exe write-host {un} {op} {cnp} {dom}', shell=True)       
    return render_template('password.html', form=form)

it doesnt work

Once a function hits a return statement, it stops executing. Anything after that isn’t going to be run.

if ...:
        return ... #function stops
        subprocess.run(...) #never runs

Those either need to be reversed so the subprocess is launched first, or combine them so they both happen in the return.

how do you make them run in one return command

Put them on the same line, separated by a comma. Just be aware that such a function will actually return a tuple, so you need to either discard the second result or deal with it.

>>> def foo():
...  print('spam')
...
>>> def bar():
...  return 'bar', foo()
...
>>> bar()
spam
('bar', None)
>>> egg, _ = bar()
spam
>>> egg
'bar'

ok managed to get it to work

if request.method == 'POST' and form.validate():
        subprocess.run(f'powershell.exe write-host {form.un.data} {form.op.data} {form.cnp.data} {form.dom.data}', shell=True)
        return '<h1>The username is {}. The old password is {}. the new password is {}. changing for domain {}'.format(form.un.data, form.op.data, form.cnp.data, form.dom.data)
    return render_template('password.html', form=form)

whats a tuple

ok i got it working going to close this