current_user.is_authenticated始终为False(TACACS和flask_login)

时间:2020-03-10 18:03:16

标签: python-3.x flask flask-login

摘要

我正在尝试实现基于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设置为等于adminuser


当我使用有效的凭据登录时,这是从控制台中看到的内容:

User LetMeIn logged in. User authenticated: True
Is current_user authenticated? False
User is authorized to view test.html.

...在浏览器的test.html页面上,我看到

html_output


1 个答案:

答案 0 :(得分:0)

我在四次解析代码后就发现了这一点。

简而言之,我错过了login_user()函数调用。这就是将自定义User类与flask_login current_user关联关联的原因。

我还需要创建一个简单的本地用户名存储方法,以便所需的load_user函数可以正常运行。为此,我添加了一个可全局访问的字典,该字典将我的自定义User对象存储为与保存用户名的键相关联的值。

我将通过这些更改来更新原始帖子中的代码片段,以期我的努力对将来的某人有用。我找不到有关将TACACS与flask_login集成的在线信息。