в login_user, если не принудительно и не user.is_active: AttributeError: у объекта 'NoneType' нет атрибута 'is_active' - PullRequest
0 голосов
/ 19 июня 2019

Здравствуйте. У меня проблемы с настройкой активности моего пользователя в моем проекте при попытке войти в систему через Google.Я использую WTF для форм, и я думаю, что это может не возвращать обратно приложению, что пользователь фактически активен.

Я уже добавил UserMixin и попробовал другие небольшие фрагменты кода, которые у меня естьвидел без удачи.


import os
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
from flask import Flask, render_template, redirect, url_for
from flask_bootstrap import Bootstrap
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField
from wtforms.validators import InputRequired, Email, Length
from flask_sqlalchemy  import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
from flask_admin import Admin, AdminIndexView
from flask_admin.contrib.sqla import ModelView
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from flask_dance.contrib.google import make_google_blueprint, google
from flask_dance.consumer.storage.sqla import OAuthConsumerMixin, SQLAlchemyStorage
from flask_dance.consumer import oauth_authorized
from sqlalchemy.orm.exc import NoResultFound


app = Flask(__name__)

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = 'hidden'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////sqlite3/login.db'
bootstrap = Bootstrap(app)
db = SQLAlchemy(app)


app.config["GOOGLE_OAUTH_CLIENT_ID"] = ("hidden")
app.config["GOOGLE_OAUTH_CLIENT_SECRET"] = ("hidden")
google_bp = make_google_blueprint(scope=["https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email"])
app.register_blueprint(google_bp, url_prefix="/google_login")

login_manager = LoginManager(app)
login_manager.init_app(app)
login_manager.login_view = 'login'


class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(15), unique=True)
    email = db.Column(db.String(50), unique=True)
    password = db.Column(db.String(80))

    def __init__(self, username, email, password):
        self.username = username
        self.email = email
        self.password = password

    def is_authenticated(self):
        return True

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def get_id(self):
        return str(self.id)

    def __repr__(self):
        return '<User %r>' %(self.username)


class OAuth(OAuthConsumerMixin, db.Model):
    user_id = db.Column(db.Integer, db.ForeignKey(User.id))
    user = db.relationship(User)


@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))


google_bp.storage = SQLAlchemyStorage(OAuth, db.session, user=current_user)


class MyModelView(ModelView):
    def is_accessible(self):
        return current_user.is_authenciated

    def inaccessible_callback(self, name, **kwargs):
        return redirect(url_for('login'))


class MyAdminIndexView(AdminIndexView):
    def is_accessible(self):
        return current_user.is_authenticated


admin = Admin(app, index_view=MyAdminIndexView())
admin.add_view(ModelView(User, db.session))
admin.add_view(ModelView(OAuth, db.session))


class LoginForm(FlaskForm):
    username = StringField('username', validators=[InputRequired(), Length(min=4, max=15)])
    password = PasswordField('password', validators=[InputRequired(), Length(min=8, max=80)])
    remember = BooleanField('remember me')


class RegisterForm(FlaskForm):
    email = StringField('email', validators=[InputRequired(), Email(message='Invalid email'), Length(max=50)])
    username = StringField('username', validators=[InputRequired(), Length(min=4, max=15)])
    password = PasswordField('password', validators=[InputRequired(), Length(min=8, max=80)])


@app.route('/')
def index():
    return render_template('index.html')


@app.route("/google")
def google_login():
    if not google.authorized:
        return redirect(url_for('google.login'))
    return redirect(url_for('dashboard'))


@oauth_authorized.connect_via (google_bp)
def google_is_logged_in(blueprint, token):
        account_info = blueprint.session.get('/oauth2/v2/userinfo')
        if account_info.ok:
            account_info_json = account_info.json()
            form.username.data = account_info_json['name']
            query = User.query.filter_by(username=form.username.data)
            try:
                user = query.first()
            except NoResultFound:
                user = User(username=form.username.data)
                db.session.add(user)
                db.session.commit()
            login_user(user)

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()

    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        user.is_active = True

        if user:
            if check_password_hash(user.password, form.password.data):
                login_user(user, remember=form.remember.data)
                return redirect(url_for('dashboard'))

        if not user:
            user = User(username=form.username.data)
            db.session.add(user)
            db.session.commit()

        login_user(user)

        return '<h1>Invalid username or password</h1>'

        #return '<h1>' + form.username.data + ' ' + form.password.data + '</h1>'
    return render_template('login.html', form=form)


@app.route('/signup', methods=['GET', 'POST'])
def signup():
    form = RegisterForm()

    if form.validate_on_submit():
        hashed_password = generate_password_hash(form.password.data, method='sha256')
        new_user = User(username=form.username.data, email=form.email.data, password=hashed_password)
        db.session.add(new_user)
        db.session.commit()
        return '<h1>New user has been created!</h1>'
        #return '<h1>' + form.username.data + ' ' + form.email.data + ' ' + form.password.data + '</h1>'

    return render_template('signup.html', form=form)


@app.route('/dashboard')
@login_required
def dashboard():
    return render_template('dashboard.html', name=current_user.username)


@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('index'))


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


 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [18/Jun/2019 16:09:23] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [18/Jun/2019 16:09:24] "GET /login HTTP/1.1" 200 -
127.0.0.1 - - [18/Jun/2019 16:09:26] "POST /login HTTP/1.1" 302 -
127.0.0.1 - - [18/Jun/2019 16:09:26] "GET /dashboard HTTP/1.1" 200 -
127.0.0.1 - - [18/Jun/2019 16:09:28] "GET /google HTTP/1.1" 302 -
127.0.0.1 - - [18/Jun/2019 16:09:28] "GET /google_login/google HTTP/1.1" 302 -
127.0.0.1 - - [18/Jun/2019 16:09:29] "GET /google HTTP/1.1" 302 -
127.0.0.1 - - [18/Jun/2019 16:09:29] "GET /google_login/google HTTP/1.1" 302 -
[2019-06-18 16:09:33,406] ERROR in app: Exception on /google_login/google/authorized [GET]
Traceback (most recent call last):
  File "D:\PyCharm Projects\Plutone\lib\site-packages\flask\app.py", line 2311, in wsgi_app
    response = self.full_dispatch_request()
  File "D:\PyCharm Projects\Plutone\lib\site-packages\flask\app.py", line 1834, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "D:\PyCharm Projects\Plutone\lib\site-packages\flask\app.py", line 1737, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "D:\PyCharm Projects\Plutone\lib\site-packages\flask\_compat.py", line 36, in reraise
    raise value
  File "D:\PyCharm Projects\Plutone\lib\site-packages\flask\app.py", line 1832, in full_dispatch_request
    rv = self.dispatch_request()
  File "D:\PyCharm Projects\Plutone\lib\site-packages\flask\app.py", line 1818, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "D:\PyCharm Projects\Plutone\lib\site-packages\flask_dance\consumer\oauth2.py", line 260, in authorized
    **self.token_url_params
  File "D:\PyCharm Projects\Plutone\lib\site-packages\requests_oauthlib\oauth2_session.py", line 307, in fetch_token
    self._client.parse_request_body_response(r.text, scope=self.scope)
  File "D:\PyCharm Projects\Plutone\lib\site-packages\oauthlib\oauth2\rfc6749\clients\base.py", line 415, in parse_request_body_response
    self.token = parse_token_response(body, scope=scope)
  File "D:\PyCharm Projects\Plutone\lib\site-packages\oauthlib\oauth2\rfc6749\parameters.py", line 425, in parse_token_response
    validate_token_parameters(params)
  File "D:\PyCharm Projects\Plutone\lib\site-packages\oauthlib\oauth2\rfc6749\parameters.py", line 455, in validate_token_parameters
    raise w
Warning: Scope has changed from "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email openid" to "https://www.googleapis.com/auth/userinfo.profile openid https://www.googleapis.com/auth/userinfo.email".
127.0.0.1 - - [18/Jun/2019 16:09:33] "GET /google_login/google/authorized?state=HaYEFK78Lukst0PMut20qJFF9YJWjf&code=4%2FbQGtUnPEpVJXyS_K0xcDGEY-droq9RS1_P_hwsHRBgLrtPfg2_AijJc7cdxAz4dxqSwKsVIg67i3a7tO18nKXbU&scope=email+profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+openid&authuser=0&session_state=d2e6086c6a76a0734222d234e793186ec8535a86..f857&prompt=consent HTTP/1.1" 500 -

1 Ответ

0 голосов
/ 19 июня 2019

Полученная ошибка относится к объекту NoneType, то есть вы не получили пользователя при попытке вызвать login_user(user) (пользователь - None).

Возвращаясь к вашему коду, главная проблема заключается в том, что ваш запрос не восстанавливает запись пользователя. Убедитесь, что json верен и содержит ключ 'name', который вы ищете.

Обратите внимание, что query.first() вернет None, если записи не найдены. Это не приведет к исключению, и вы можете сделать что-то вроде:

...

user = User.query.filter_by(username=username).first()

if not user:
    user = User(username=username)
    db.session.add(user)
    db.session.commit()

login_user(user)
...