我正在尝试实现基于TACACS +的flask_login系统,但由于current_user.is_authenticated
始终为假,因此我陷入了困境。我没有使用任何本地数据库-通过我的登录表单提交的用户名和密码将直接传递到TACACS。
我的自定义User
类实现了flask_login文档here中描述的3个属性和1个方法:
下面,您将找到每个文件的相关代码,因此可以在自己的环境中进行设置。
下面有很多信息,所以我想尽可能清楚地分享我的实际问题:
User
类连接到current_user
代理?实现的基本问题是,当我使用@login_required
装饰器转到任何烧瓶路径时,应用程序都认为用户未登录,并将其重定向到登录页面。我已经确定是因为current_user.is_authenticated
属性从不为True。
app.py
from flask import Flask, request, render_template, url_for, flash, redirect, session, Markup
from forms import LoginForm
from flask_login import LoginManager, login_user, logout_user, login_required, current_user, UserMixin
import user_auth as auth
# TODO: Save the user_dict to a pickle file so that users persist between service restarts
user_dict = {}
class User():
def __init__(self, username, password):
self.username = username
self.password = password
# Send TACACS authentication and authorization requests
priv = auth.login(self.username, self.password)
if priv:
self.is_authenticated = True
self.is_active = True
self.is_anonymous = False
if priv == 'admin':
self.priv_lvl = 15
elif priv == 'user':
self.priv_lvl = 10
elif priv == 'employee':
self.priv_lvl = 5
else:
self.is_authenticated = False
self.is_anonymous = True
self.priv_lvl = -1
def get_id(self):
return self.username
app = Flask(__name__)
app.secret_key = 'mysecretkey!'
app.static_url_path = 'static/'
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'index'
@login_manager.user_loader
def load_user(user):
global user_dict
if user in user_dict.keys():
return user_dict[user]
@app.route('/', methods=['GET', 'POST'])
def index():
form = LoginForm()
try:
if user.is_authenticated:
return render_template('test.html')
except UnboundLocalError:
user = None
pass
if form.validate_on_submit():
username = form.username.data
password = form.password.data
user = User(username, password)
username = password = None
print(f"User {user.username} logged in. User authenticated: {user.is_authenticated}")
print(f"Is current_user authenticated? {current_user.is_authenticated}")
if user.priv_lvl >= 0:
# SOLUTION -> The following was missing
login_user(user, remember=form.remember.data)
user_dict.update({
user.username : user
})
# END SOLUTION
print("User is authorized to view test.html.")
return render_template('test.html', current_user=current_user, user=user)
else:
flash(f'Invalid login', 'error')
return render_template('index.html', title='Login Required', form=form, user=user)
return render_template('index.html', title='Login Required', form=form, user=user)
@app.route("/home", methods=['GET'])
@login_required
def home():
return render_template('test.html')
user_auth.py
from tacacs_plus.client import TACACSClient
from tacacs_plus.flags import TAC_PLUS_ACCT_FLAG_START, TAC_PLUS_ACCT_FLAG_WATCHDOG, TAC_PLUS_ACCT_FLAG_STOP
import socket
ISE = 'my.ip.add.rr'
auth_key = 'password'
def login(username, password):
cli = TACACSClient(ISE, 49, auth_key, timeout=10, family=socket.AF_INET)
authen = cli.authenticate(username, password)
if authen.valid:
author = cli.authorize(username, arguments=[b"service=", b"protocol="])
if author.valid:
role = author.arguments[0].decode('utf-8')
if 'user' in role.lower():
priv = 'user'
elif 'admin' in role.lower():
priv = 'admin'
else:
print("User has authenticated successfully, but failed authorization.")
priv = 'employee'
else:
print("User failed authentication.")
priv = None
return priv
forms.py
from flask_wtf import FlaskForm
from wtforms import Form, StringField, SubmitField, BooleanField
from wtforms.fields import PasswordField
from wtforms.validators import DataRequired
class LoginForm(FlaskForm):
username = StringField("Username", validators=[DataRequired()])
password = PasswordField("Password", validators=[DataRequired()])
remember = BooleanField("Remember Me")
submit = SubmitField("Login")
index.html
<div id='login-container' class='container'>
<form method="POST" action="">
{{ form.csrf_token }}
{{ form.hidden_tag() }}
<fieldset>
<legend class="border-bottom mb-4">Login</legend>
<p>Use your TACACS credentials.</p>
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alerts">
{% for message in messages %}
<div class="alert alert-warning" role="alert">{{ message }}</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<div class='form-group'>
{{ form.username.label(class="form-control-label") }}
{% if form.username.errors %}
{{ form.username(class="form-control form-control-lg is-invalid") }}
<div class='custom-invalid-feedback'>
{% for error in form.username.errors %}
<span>
{{ error }}
</span>
{% endfor %}
</div>
{% else %}
{{ form.username(class="form-control form-control-lg") }}
{% endif %}
</div>
<div class='form-group'>
{{ form.password.label(class="form-control-label") }}
{% if form.password.errors %}
{{ form.password(class="form-control form-control-lg is-invalid") }}
<div class='custom-invalid-feedback'>
{% for error in form.password.errors %}
<span>
{{ error }}
</span>
{% endfor %}
</div>
{% else %}
{{ form.password(class="form-control form-control-lg") }}
{% endif %}
</div>
{{form.remember.label}}
{{form.remember}}
<div class="form-group">
{{ form.submit(class="btn btn-outline-info") }}
</div>
</fieldset>
</form>
</div>
test.html
{% if user.is_authenticated %}
<h2>My custom user class is authenticated</h2>
{% else %}
<h2> My custom user class is NOT authenticated</h2>
{% endif %}
{% if current_user.is_authenticated %}
<h2> current_user is authenticated</h2>
{% else %}
<h2>current_user is NOT authenticated</h2>
{% endif %}
requirements.txt(以防您要在自己的环境中进行测试)
Jinja2==2.10
dominate==2.5.1
Flask==1.1.1
flask_login==0.5.0
flask_wtf==0.14.3
plotly==4.5.3
tacacs_plus==2.6
WTForms==2.2.1
注意:如果要在自己的应用程序中进行测试并且不想打扰TACACS,请手动将priv
设置为等于admin
或user
当我使用有效的凭据登录时,这是从控制台中看到的内容:
User LetMeIn logged in. User authenticated: True
Is current_user authenticated? False
User is authorized to view test.html.
...在浏览器的test.html页面上,我看到
答案 0 :(得分:0)
我在四次解析代码后就发现了这一点。
简而言之,我错过了login_user()函数调用。这就是将自定义User类与flask_login current_user
关联关联的原因。
我还需要创建一个简单的本地用户名存储方法,以便所需的load_user
函数可以正常运行。为此,我添加了一个可全局访问的字典,该字典将我的自定义User对象存储为与保存用户名的键相关联的值。
我将通过这些更改来更新原始帖子中的代码片段,以期我的努力对将来的某人有用。我找不到有关将TACACS与flask_login集成的在线信息。