You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by fo...@apache.org on 2018/03/07 08:50:00 UTC
incubator-airflow git commit: [AIRFLOW-2181] Convert password_auth
and test_password_endpoints from DOS to UNIX
Repository: incubator-airflow
Updated Branches:
refs/heads/master b6f9ba9cc -> 7cba83333
[AIRFLOW-2181] Convert password_auth and test_password_endpoints from DOS to UNIX
Closes #3102 from dan-sf/AIRFLOW-2181
Project: http://git-wip-us.apache.org/repos/asf/incubator-airflow/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-airflow/commit/7cba8333
Tree: http://git-wip-us.apache.org/repos/asf/incubator-airflow/tree/7cba8333
Diff: http://git-wip-us.apache.org/repos/asf/incubator-airflow/diff/7cba8333
Branch: refs/heads/master
Commit: 7cba83333c5227ce37967c65d189a5e994898c68
Parents: b6f9ba9
Author: dan-sf <fo...@gmail.com>
Authored: Wed Mar 7 09:49:48 2018 +0100
Committer: Fokko Driesprong <fo...@godatadriven.com>
Committed: Wed Mar 7 09:49:48 2018 +0100
----------------------------------------------------------------------
airflow/contrib/auth/backends/password_auth.py | 418 +++++++++----------
.../api/experimental/test_password_endpoints.py | 162 +++----
2 files changed, 290 insertions(+), 290 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-airflow/blob/7cba8333/airflow/contrib/auth/backends/password_auth.py
----------------------------------------------------------------------
diff --git a/airflow/contrib/auth/backends/password_auth.py b/airflow/contrib/auth/backends/password_auth.py
index 33f3ae0..cfb921c 100644
--- a/airflow/contrib/auth/backends/password_auth.py
+++ b/airflow/contrib/auth/backends/password_auth.py
@@ -1,209 +1,209 @@
-# -*- coding: utf-8 -*-
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from __future__ import unicode_literals
-
-from sys import version_info
-
-import base64
-import flask_login
-from flask_login import current_user
-from flask import flash, Response
-from wtforms import Form, PasswordField, StringField
-from wtforms.validators import InputRequired
-from functools import wraps
-
-from flask import url_for, redirect, make_response
-from flask_bcrypt import generate_password_hash, check_password_hash
-
-from sqlalchemy import Column, String
-from sqlalchemy.ext.hybrid import hybrid_property
-
-from airflow import settings
-from airflow import models
-from airflow.utils.db import provide_session
-from airflow.utils.log.logging_mixin import LoggingMixin
-
-login_manager = flask_login.LoginManager()
-login_manager.login_view = 'airflow.login' # Calls login() below
-login_manager.login_message = None
-
-log = LoggingMixin().log
-PY3 = version_info[0] == 3
-
-
-class AuthenticationError(Exception):
- pass
-
-
-class PasswordUser(models.User):
- _password = Column('password', String(255))
-
- def __init__(self, user):
- self.user = user
-
- @hybrid_property
- def password(self):
- return self._password
-
- @password.setter
- def _set_password(self, plaintext):
- self._password = generate_password_hash(plaintext, 12)
- if PY3:
- self._password = str(self._password, 'utf-8')
-
- def authenticate(self, plaintext):
- return check_password_hash(self._password, plaintext)
-
- def is_active(self):
- '''Required by flask_login'''
- return True
-
- def is_authenticated(self):
- '''Required by flask_login'''
- return True
-
- def is_anonymous(self):
- '''Required by flask_login'''
- return False
-
- def get_id(self):
- '''Returns the current user id as required by flask_login'''
- return str(self.id)
-
- def data_profiling(self):
- '''Provides access to data profiling tools'''
- return True
-
- def is_superuser(self):
- '''Access all the things'''
- return True
-
-
-@login_manager.user_loader
-@provide_session
-def load_user(userid, session=None):
- log.debug("Loading user %s", userid)
- if not userid or userid == 'None':
- return None
-
- user = session.query(models.User).filter(models.User.id == int(userid)).first()
- return PasswordUser(user)
-
-
-def authenticate(session, username, password):
- """
- Authenticate a PasswordUser with the specified
- username/password.
-
- :param session: An active SQLAlchemy session
- :param username: The username
- :param password: The password
-
- :raise AuthenticationError: if an error occurred
- :return: a PasswordUser
- """
- if not username or not password:
- raise AuthenticationError()
-
- user = session.query(PasswordUser).filter(
- PasswordUser.username == username).first()
-
- if not user:
- raise AuthenticationError()
-
- if not user.authenticate(password):
- raise AuthenticationError()
-
- log.info("User %s successfully authenticated", username)
- return user
-
-
-@provide_session
-def login(self, request, session=None):
- if current_user.is_authenticated():
- flash("You are already logged in")
- return redirect(url_for('admin.index'))
-
- username = None
- password = None
-
- form = LoginForm(request.form)
-
- if request.method == 'POST' and form.validate():
- username = request.form.get("username")
- password = request.form.get("password")
-
- try:
- user = authenticate(session, username, password)
- flask_login.login_user(user)
-
- return redirect(request.args.get("next") or url_for("admin.index"))
- except AuthenticationError:
- flash("Incorrect login details")
- return self.render('airflow/login.html',
- title="Airflow - Login",
- form=form)
- finally:
- session.commit()
- session.close()
-
-
-class LoginForm(Form):
- username = StringField('Username', [InputRequired()])
- password = PasswordField('Password', [InputRequired()])
-
-
-def _unauthorized():
- """
- Indicate that authorization is required
- :return:
- """
- return Response("Unauthorized", 401, {"WWW-Authenticate": "Basic"})
-
-
-def _forbidden():
- return Response("Forbidden", 403)
-
-
-def init_app(app):
- pass
-
-
-def requires_authentication(function):
- @wraps(function)
- def decorated(*args, **kwargs):
- from flask import request
-
- header = request.headers.get("Authorization")
- if header:
- userpass = ''.join(header.split()[1:])
- username, password = base64.b64decode(userpass).decode("utf-8").split(":", 1)
-
- session = settings.Session()
- try:
- authenticate(session, username, password)
-
- response = function(*args, **kwargs)
- response = make_response(response)
- return response
-
- except AuthenticationError:
- return _forbidden()
-
- finally:
- session.commit()
- session.close()
- return _unauthorized()
- return decorated
+# -*- coding: utf-8 -*-
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import unicode_literals
+
+from sys import version_info
+
+import base64
+import flask_login
+from flask_login import current_user
+from flask import flash, Response
+from wtforms import Form, PasswordField, StringField
+from wtforms.validators import InputRequired
+from functools import wraps
+
+from flask import url_for, redirect, make_response
+from flask_bcrypt import generate_password_hash, check_password_hash
+
+from sqlalchemy import Column, String
+from sqlalchemy.ext.hybrid import hybrid_property
+
+from airflow import settings
+from airflow import models
+from airflow.utils.db import provide_session
+from airflow.utils.log.logging_mixin import LoggingMixin
+
+login_manager = flask_login.LoginManager()
+login_manager.login_view = 'airflow.login' # Calls login() below
+login_manager.login_message = None
+
+log = LoggingMixin().log
+PY3 = version_info[0] == 3
+
+
+class AuthenticationError(Exception):
+ pass
+
+
+class PasswordUser(models.User):
+ _password = Column('password', String(255))
+
+ def __init__(self, user):
+ self.user = user
+
+ @hybrid_property
+ def password(self):
+ return self._password
+
+ @password.setter
+ def _set_password(self, plaintext):
+ self._password = generate_password_hash(plaintext, 12)
+ if PY3:
+ self._password = str(self._password, 'utf-8')
+
+ def authenticate(self, plaintext):
+ return check_password_hash(self._password, plaintext)
+
+ def is_active(self):
+ '''Required by flask_login'''
+ return True
+
+ def is_authenticated(self):
+ '''Required by flask_login'''
+ return True
+
+ def is_anonymous(self):
+ '''Required by flask_login'''
+ return False
+
+ def get_id(self):
+ '''Returns the current user id as required by flask_login'''
+ return str(self.id)
+
+ def data_profiling(self):
+ '''Provides access to data profiling tools'''
+ return True
+
+ def is_superuser(self):
+ '''Access all the things'''
+ return True
+
+
+@login_manager.user_loader
+@provide_session
+def load_user(userid, session=None):
+ log.debug("Loading user %s", userid)
+ if not userid or userid == 'None':
+ return None
+
+ user = session.query(models.User).filter(models.User.id == int(userid)).first()
+ return PasswordUser(user)
+
+
+def authenticate(session, username, password):
+ """
+ Authenticate a PasswordUser with the specified
+ username/password.
+
+ :param session: An active SQLAlchemy session
+ :param username: The username
+ :param password: The password
+
+ :raise AuthenticationError: if an error occurred
+ :return: a PasswordUser
+ """
+ if not username or not password:
+ raise AuthenticationError()
+
+ user = session.query(PasswordUser).filter(
+ PasswordUser.username == username).first()
+
+ if not user:
+ raise AuthenticationError()
+
+ if not user.authenticate(password):
+ raise AuthenticationError()
+
+ log.info("User %s successfully authenticated", username)
+ return user
+
+
+@provide_session
+def login(self, request, session=None):
+ if current_user.is_authenticated():
+ flash("You are already logged in")
+ return redirect(url_for('admin.index'))
+
+ username = None
+ password = None
+
+ form = LoginForm(request.form)
+
+ if request.method == 'POST' and form.validate():
+ username = request.form.get("username")
+ password = request.form.get("password")
+
+ try:
+ user = authenticate(session, username, password)
+ flask_login.login_user(user)
+
+ return redirect(request.args.get("next") or url_for("admin.index"))
+ except AuthenticationError:
+ flash("Incorrect login details")
+ return self.render('airflow/login.html',
+ title="Airflow - Login",
+ form=form)
+ finally:
+ session.commit()
+ session.close()
+
+
+class LoginForm(Form):
+ username = StringField('Username', [InputRequired()])
+ password = PasswordField('Password', [InputRequired()])
+
+
+def _unauthorized():
+ """
+ Indicate that authorization is required
+ :return:
+ """
+ return Response("Unauthorized", 401, {"WWW-Authenticate": "Basic"})
+
+
+def _forbidden():
+ return Response("Forbidden", 403)
+
+
+def init_app(app):
+ pass
+
+
+def requires_authentication(function):
+ @wraps(function)
+ def decorated(*args, **kwargs):
+ from flask import request
+
+ header = request.headers.get("Authorization")
+ if header:
+ userpass = ''.join(header.split()[1:])
+ username, password = base64.b64decode(userpass).decode("utf-8").split(":", 1)
+
+ session = settings.Session()
+ try:
+ authenticate(session, username, password)
+
+ response = function(*args, **kwargs)
+ response = make_response(response)
+ return response
+
+ except AuthenticationError:
+ return _forbidden()
+
+ finally:
+ session.commit()
+ session.close()
+ return _unauthorized()
+ return decorated
http://git-wip-us.apache.org/repos/asf/incubator-airflow/blob/7cba8333/tests/www/api/experimental/test_password_endpoints.py
----------------------------------------------------------------------
diff --git a/tests/www/api/experimental/test_password_endpoints.py b/tests/www/api/experimental/test_password_endpoints.py
index 2c2cc7f..1bf58fa 100644
--- a/tests/www/api/experimental/test_password_endpoints.py
+++ b/tests/www/api/experimental/test_password_endpoints.py
@@ -1,81 +1,81 @@
-# -*- coding: utf-8 -*-
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import json
-import unittest
-
-from datetime import datetime
-
-from airflow import models
-from airflow import configuration
-from airflow.www import app as application
-from airflow.settings import Session
-from airflow.contrib.auth.backends.password_auth import PasswordUser
-
-try:
- from ConfigParser import DuplicateSectionError
-except ImportError:
- from configparser import DuplicateSectionError
-
-
-class ApiPasswordTests(unittest.TestCase):
- def setUp(self):
- configuration.load_test_config()
- try:
- configuration.conf.add_section("api")
- except DuplicateSectionError:
- pass
-
- configuration.conf.set("api",
- "auth_backend",
- "airflow.contrib.auth.backends.password_auth")
-
- self.app = application.create_app(testing=True)
-
- session = Session()
- user = models.User()
- password_user = PasswordUser(user)
- password_user.username = 'hello'
- password_user.password = 'world'
- session.add(password_user)
- session.commit()
- session.close()
-
- def test_authorized(self):
- with self.app.test_client() as c:
- url_template = '/api/experimental/dags/{}/dag_runs'
- response = c.post(
- url_template.format('example_bash_operator'),
- data=json.dumps(dict(run_id='my_run' + datetime.now().isoformat())),
- content_type="application/json",
- headers={'Authorization': 'Basic aGVsbG86d29ybGQ='} # hello:world
- )
- self.assertEqual(200, response.status_code)
-
- def test_unauthorized(self):
- with self.app.test_client() as c:
- url_template = '/api/experimental/dags/{}/dag_runs'
- response = c.post(
- url_template.format('example_bash_operator'),
- data=json.dumps(dict(run_id='my_run' + datetime.now().isoformat())),
- content_type="application/json"
- )
-
- self.assertEqual(401, response.status_code)
-
- def tearDown(self):
- session = Session()
- session.query(models.User).delete()
- session.commit()
- session.close()
+# -*- coding: utf-8 -*-
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import unittest
+
+from datetime import datetime
+
+from airflow import models
+from airflow import configuration
+from airflow.www import app as application
+from airflow.settings import Session
+from airflow.contrib.auth.backends.password_auth import PasswordUser
+
+try:
+ from ConfigParser import DuplicateSectionError
+except ImportError:
+ from configparser import DuplicateSectionError
+
+
+class ApiPasswordTests(unittest.TestCase):
+ def setUp(self):
+ configuration.load_test_config()
+ try:
+ configuration.conf.add_section("api")
+ except DuplicateSectionError:
+ pass
+
+ configuration.conf.set("api",
+ "auth_backend",
+ "airflow.contrib.auth.backends.password_auth")
+
+ self.app = application.create_app(testing=True)
+
+ session = Session()
+ user = models.User()
+ password_user = PasswordUser(user)
+ password_user.username = 'hello'
+ password_user.password = 'world'
+ session.add(password_user)
+ session.commit()
+ session.close()
+
+ def test_authorized(self):
+ with self.app.test_client() as c:
+ url_template = '/api/experimental/dags/{}/dag_runs'
+ response = c.post(
+ url_template.format('example_bash_operator'),
+ data=json.dumps(dict(run_id='my_run' + datetime.now().isoformat())),
+ content_type="application/json",
+ headers={'Authorization': 'Basic aGVsbG86d29ybGQ='} # hello:world
+ )
+ self.assertEqual(200, response.status_code)
+
+ def test_unauthorized(self):
+ with self.app.test_client() as c:
+ url_template = '/api/experimental/dags/{}/dag_runs'
+ response = c.post(
+ url_template.format('example_bash_operator'),
+ data=json.dumps(dict(run_id='my_run' + datetime.now().isoformat())),
+ content_type="application/json"
+ )
+
+ self.assertEqual(401, response.status_code)
+
+ def tearDown(self):
+ session = Session()
+ session.query(models.User).delete()
+ session.commit()
+ session.close()