Login via Airflow Web Authentication shows an error page - flask

I enabled a Web Authentication for Airflow by following instructions at http://airflow.apache.org/security.html#web-authentication (and restarted the web server)
Logging in seems to work but what I see is an error page with this error message:
File "/usr/local/lib/python2.7/dist-packages/airflow/contrib/auth/backends/password_auth.py", line 154, in login
user = authenticate(session, username, password)
File "/usr/local/lib/python2.7/dist-packages/airflow/contrib/auth/backends/password_auth.py", line 131, in authenticate
if not user.authenticate(password):
File "/usr/local/lib/python2.7/dist-packages/airflow/contrib/auth/backends/password_auth.py", line 72, in authenticate
return check_password_hash(self._password, plaintext)
File "/usr/local/lib/python2.7/dist-packages/flask_bcrypt.py", line 67, in check_password_hash
return Bcrypt().check_password_hash(pw_hash, password)
File "/usr/local/lib/python2.7/dist-packages/flask_bcrypt.py", line 193, in check_password_hash
return safe_str_cmp(bcrypt.hashpw(password, pw_hash), pw_hash)
File "/usr/local/lib/python2.7/dist-packages/bcrypt/__init__.py", line 81, in hashpw
original_salt, salt = salt, _normalize_re.sub(b"$2b$", salt)
TypeError: expected string or buffer
Any idea/insight on this issue?

As mentioned here, using the following worked for me when I had the same error:
import airflow
from airflow import models, settings
from airflow.contrib.auth.backends.password_auth import PasswordUser
from flask_bcrypt import generate_password_hash
user = PasswordUser(models.User())
user.username = 'some'
user.email = 'some#email.com'
user._password = generate_password_hash('password', 12).decode('utf-8')
session = settings.Session()
session.add(user)
session.commit()
session.close()

Related

How to use oidc (OpenIDConnect object) in flask.Blueprint when using flask.create_app?

I want to use #oidc.require_login to redirect login request to okta. I get AttributeError: '_AppCtxGlobals' object has no attribute 'oidc_id_token' error which I am unable to resolve
1) I built an app based on Flaskr tutorial that demonstrates use of flask factory method create_app.
2) I created a okta.py class as follows:
from oauth2client.client import OAuth2Credentials
from flask_oidc import OpenIDConnect
from okta import UsersClient
import click
from flask import current_app, g, session
from flask.cli import with_appcontext
oidc = OpenIDConnect()
def get_oidc():
"""
Connect to okta
"""
if 'oidc' not in g:
print('okta: get_oidc call')
g.oidc = OpenIDConnect(current_app)
g.okta_client = UsersClient("<okta-server-url>", "<secret>")
# fixing global oidc problem for decorator in rooms
oidc = g.oidc
return g.oidc
def close_oidc(app):
""" Release okta connection
"""
oidc = g.pop('oidc',None)
if oidc is not None:
oidc.logout()
# with app.app_context():
# session.clear()
def init_okta():
"""Connect to existing table"""
oidc = get_oidc()
""" Can do additional initialization if required """
#click.command('init-okta')
#with_appcontext
def init_okta_command():
"""Connect to existing oidc"""
init_okta()
click.echo (get_oidc())
click.echo('Initialized Okta.')
print('Initialized Okta.')
def init_app(app):
"""Register okta functions with the Flask app. This is called by
the application factory.
"""
app.teardown_appcontext(close_oidc)
app.cli.add_command(init_okta_command)
3) My goal is to use okta login to browse room listings
from flask import (
Blueprint, flash, g, redirect, render_template,
request, url_for, current_app, session, jsonify
)
from werkzeug.exceptions import abort
...
from hotel.okta import oidc, get_oidc, init_app
bp = Blueprint('rooms', __name__)
...
#bp.route('/login', methods=['GET', 'POST'])
#oidc.require_login
def login():
"""
Force the user to login, then redirect them to the get_books.
Currently this code DOES NOT work
Problem:
* oidc global object is not available to pass request to okta
Resolution:
* redirecting to rooms.calendar
"""
# info = oidc.user_getinfo(['preferred_username', 'email', 'sub'])
# id_token = OAuth2Credentials.from_json(oidc.credentials_store[info.get('sub')]).token_response['id_token']
return redirect(url_for("rooms.calendar"))
4) My __init__.py looks like this
import os
from flask import Flask
from flask_oidc import OpenIDConnect
from okta import UsersClient
# This is a factory method for productive deployment
# Use app specific configuration
# For any app local files, use /instnce Folder
def create_app(test_config=None):
"""Create and configure an instance of the Flask application."""
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
# a default secret that should be overridden by instance config
SECRET_KEY='dev',
# store the database in the instance folder
DATABASE=os.path.join(app.instance_path, 'hotel.sqlite'),
OIDC_CLIENT_SECRETS=os.path.join(app.instance_path, 'client_secrets.json'),
OIDC_COOKIE_SECURE=False,
OIDC_CALLBACK_ROUTE= '/oidc/callback',
OIDC_SCOPES=["openid", "email", "profile"],
OIDC_ID_TOKEN_COOKIE_NAME = 'oidc_token',
)
if test_config is None:
# load the instance config, if it exists, when not testing
app.config.from_pyfile('config.py', silent=True)
else:
# load the test config if passed in
app.config.update(test_config)
# ensure the instance folder exists
try:
os.makedirs(app.instance_path)
except OSError:
pass
# # register the database commands
from hotel import db
db.init_app(app)
# apply the blueprints to the app
from hotel import rooms
app.register_blueprint(rooms.bp)
# for Okta
# Ref: https://www.fullstackpython.com/blog/add-user-authentication-flask-apps-okta.html
from hotel import okta
with app.app_context():
okta.init_app(app)
#app.route('/hello') # For testing factory method
def hello():
return 'Hello, World!'
# make url_for('index') == url_for('blog.index')
# in another app, you might define a separate main index here with
# app.route, while giving the blog blueprint a url_prefix, but for
# the tutorial the blog will be the main index
app.add_url_rule('/', endpoint='index')
return app
5) Here is the code snippet of rooms.before_request
#bp.before_request
def before_request():
print ('rooms.before_request call reached')
with current_app.app_context():
print ('rooms.before_request in app_context',g)
oidc = g.pop('oidc',None)
okta_client = g.pop('okta_client',None)
if oidc is not None and okta_client is not None:
print ('rooms.before_request g.oidc and g.okta_client available')
if oidc.user_loggedin:
# OpenID Token as
g.user = okta_client.get_user(oidc.user_getfield("sub"))
g.oidc_id_token = OAuth2Credentials.from_json(g.oidc.credentials_store[info.get('sub')]).token_response['id_token']
else:
g.user = None
else:
print('rooms.beforerequest No user logged in')
g.user = None
My Analysis:
okta.init_app(app) is expected to use client_secrets.json in /instance folder to connect to okta.
In order to make #oidc.require_login to work, I exposed oidc in okta.get_oidc and import this into my rooms.py code
However, this does not work :(! Stack trace is:
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 2328, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 2314, in wsgi_app
response = self.handle_exception(e)
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 1760, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/_compat.py", line 36, in reraise
raise value
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 2311, in wsgi_app
response = self.full_dispatch_request()
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 1834, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 1737, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/_compat.py", line 36, in reraise
raise value
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 1832, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 1818, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask_oidc/__init__.py", line 485, in decorated
if g.oidc_id_token is None:
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/werkzeug/local.py", line 348, in __getattr__
return getattr(self._get_current_object(), name)
AttributeError: '_AppCtxGlobals' object has no attribute 'oidc_id_token'
My questions:
How can I call #oidc.require_login in flask application factory
method?
Is the way I initialize okta connection correct?
Is there anyway to manually call external authorization server without decorator approach? How?
Is setting flask stack variables a better way to go forward? If so, how?
Trying to set g in before_request does not seem to work. What are the other options to work in Flask's App Factory approach?
Any insights will be much appreciated!
Thanks!
Yuva

Api with flask-jwt-extended with authentication problems?

I have built an api with flask-restful and flask-jwt-extended and have correctly configured the validation passages for token expiration and invalidation. However, even though it has built the token expiration and invalid validation callbacks, api does not process correctly and reports the error: Signature has expired
On the server in the cloud, we have a Centos 7 x64 of 16gb ram, running the application using gunicorn in version 19.9.0. Using the miniconda to create the applications' python environments.
In tests in the production environment, the application complains of the expired token. However in a test environment, using Ubuntu 18.04.2, x64 with 16 gb ram, using the same settings with miniconda and gunicorn, the application has no problems executing it, returning the correct message when the token expires.
My jwt.py
from flask import Blueprint, Response, json, request
from flask_jwt_extended import (JWTManager, create_access_token,
create_refresh_token, get_jwt_identity,
jwt_required)
from app.models.core import User
from .schemas import UserSchema
from .utils import send_reponse, user_roles
def configure_jwt(app):
JWT = JWTManager(app)
#JWT.expired_token_loader
def my_expired_token_callback(expired_token):
return Response(
response=json.dumps({
"message": "Expired token"
}),
status=401,
mimetype='application/json'
)
#JWT.invalid_token_loader
def my_invalid_token_callback(invalid_token):
return Response(
response=json.dumps({
"message": "Invalid token"
}),
status=422,
mimetype='application/json'
)
Error log:
[2019-05-23 15:42:02 -0300] [3745] [ERROR] Exception on /api/company [POST]
Traceback (most recent call last):
File "/home/company/miniconda3/envs/api_ms/lib/python3.6/site-packages/flask/app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
File "/home/company/miniconda3/envs/api_ms/lib/python3.6/site-packages/flask/app.py", line 1799, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/home/company/miniconda3/envs/api_ms/lib/python3.6/site-packages/flask_restful/__init__.py", line 458, in wrapper
resp = resource(*args, **kwargs)
File "/home/company/miniconda3/envs/api_ms/lib/python3.6/site-packages/flask/views.py", line 88, in view
return self.dispatch_request(*args, **kwargs)
File "/home/company/miniconda3/envs/api_ms/lib/python3.6/site-packages/flask_restful/__init__.py", line 573, in dispatch_request
resp = meth(*args, **kwargs)
File "/home/company/miniconda3/envs/api_ms/lib/python3.6/site-packages/flask_jwt_extended/view_decorators.py", line 102, in wrapper
verify_jwt_in_request()
File "/home/company/miniconda3/envs/api_ms/lib/python3.6/site-packages/flask_jwt_extended/view_decorators.py", line 31, in verify_jwt_in_request
jwt_data = _decode_jwt_from_request(request_type='access')
File "/home/company/miniconda3/envs/api_ms/lib/python3.6/site-packages/flask_jwt_extended/view_decorators.py", line 266, in _decode_jwt_from_request
decoded_token = decode_token(encoded_token, csrf_token)
File "/home/company/miniconda3/envs/api_ms/lib/python3.6/site-packages/flask_jwt_extended/utils.py", line 107, in decode_token
allow_expired=allow_expired
File "/home/company/miniconda3/envs/api_ms/lib/python3.6/site-packages/flask_jwt_extended/tokens.py", line 138, in decode_jwt
leeway=leeway, options=options)
File "/home/company/miniconda3/envs/api_ms/lib/python3.6/site-packages/jwt/api_jwt.py", line 104, in decode
self._validate_claims(payload, merged_options, **kwargs)
File "/home/company/miniconda3/envs/api_ms/lib/python3.6/site-packages/jwt/api_jwt.py", line 134, in _validate_claims
self._validate_exp(payload, now, leeway)
File "/home/company/miniconda3/envs/api_ms/lib/python3.6/site-packages/jwt/api_jwt.py", line 175, in _validate_exp
raise ExpiredSignatureError('Signature has expired')
jwt.exceptions.ExpiredSignatureError: Signature has expired
I'm trying to understand why the application is able to correctly return the token expiration message in the test environment, where in the production environment it returns the error code 500 Internal Server Error. In addition to fixing this problem in our application.
Based on this link found inside the project repository, I discovered that the problem is related to the flask configuration option called PROPAGATE_EXCEPTIONS, which must be True.
The issue in the flask-jwt-extended repository that helped me find the answer.
This comment states that Flask Restful needs to ignore JWT and JWT Extended Exceptions and provides a simple snippet that solves the issue.
Copying the code from above link,
from flask_jwt_extended.exceptions import JWTExtendedException
from jwt.exceptions import PyJWTError
class FixedApi(Api):
def error_router(self, original_handler, e):
if not isinstance(e, PyJWTError) and not isinstance(e, JWTExtendedException) and self._has_fr_route():
try:
return self.handle_error(e)
except Exception:
pass # Fall through to original handler
return original_handler(e)

Trying to Access Google Directory API via p12 throws not authorized error

from apiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials
import json
import base64
import httplib2
f = file('tara1-553d334b8702.p12', 'rb')
key = f.read()
f.close()
credentials = ServiceAccountCredentials.from_p12_keyfile(
'googleapptara#tara1-167105.iam.gserviceaccount.com',
'tara1-554d335b8702.p12',
private_key_password='notasecret',
scopes=['https://www.googleapis.com/auth/admin.directory.user.readonly','https://www.googleapis.com/auth/admin.directory.user']
)
delegated_credentials=credentials.create_delegated('tara.gurung#orgemail.net)
http = httplib2.Http()
http = credentials.authorize(http)
DIRECOTORY = build('admin', 'directory_v1', credentials=credentials)
maxResults=10,orderBy='email').execute()
results = DIRECOTORY.users().list(domain='domainnamehere.net').execute()
users = results.get('users', [])
print users
Here ,I am trying to do the server to server authentication using the p12 security file and trying to grab all the users in the domain.
I have successfully fetched the users list by 3legs authentication,by authorizing from the browswer in the same account
But this way it's throwing me the following errors.
File "testing.py", line 41, in <module>
results = DIRECOTORY.users().list(domain='domainemailhere.net').execute()
File "/home/tara/taraproject/scripttesting/virtualenvforGapi/local/lib/python2.7/site-packages/oauth2client/_helpers.py", line 133, in positional_wrapper
return wrapped(*args, **kwargs)
File "/home/tara/taraproject/scripttesting/virtualenvforGapi/local/lib/python2.7/site-packages/googleapiclient/http.py", line 840, in execute
raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 403 when requesting https://www.googleapis.com/admin/directory/v1/users?domain=nepallink.net&alt=json returned "Not Authorized to access this resource/api">
SETUP DONE:
I have a super admin level access in the admin console.
I have also added
the scope via security>showmore>advance>manageipclient>authorize
Added the user id and scope
https://www.googleapis.com/auth/admin.directory.user.readonly
https://www.googleapis.com/auth/admin.directory.user
Added the users permission in service console and made a owner.
Admin SDK is Enabled
Where exactly am I missing the things. Why does it says I have no authority to access the resources/api
I see you are using delegated_credentials. Have you used it??
Change following line:
DIRECOTORY = build('admin', 'directory_v1', credentials=credentials)
to
DIRECOTORY = build('admin', 'directory_v1', credentials=delegated_credentials)

google admin sdk directory api 403 python

i want to use admin sdk directory api to create eamil account of users.
i am using google-api-python-client-1.2 library.
in folder /samples/service_account/tasks.py works for me.
but when i chance that file to list users from admin directory api it doesn't works and throws errors.
below is the code i am using.
import httplib2
import pprint
import sys
import inspect
from apiclient.discovery import build
from oauth2client.client import SignedJwtAssertionCredentials
def main(argv):
f = file('my-privatekey.p12', 'rb')
key = f.read()
f.close()
credentials = SignedJwtAssertionCredentials(
'my#developer.gserviceaccount.com',
key,
scope=['https://www.googleapis.com/auth/admin.directory.user', 'https://www.googleapis.com/auth/admin.directory.user.readonly'])
http = httplib2.Http()
http = credentials.authorize(http)
service = build("admin", "directory_v1", http)
list_of_apis = service.users().list(domain='mydomain.com').execute(http=http)
pprint.pprint(list_of_apis)
if __name__ == '__main__':
main(sys.argv)
when i run the above code i get below errors.
$python tasks.py
No handlers could be found for logger "oauth2client.util"
Traceback (most recent call last):
File "tasks.py", line 77, in <module>
main(sys.argv)
File "tasks.py", line 66, in main
list_of_apis = service.users().list(domain='messycoders.com').execute(http=http)
File "/usr/local/lib/python2.7/dist-packages/oauth2client/util.py", line 132, in positional_wrapper
return wrapped(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/apiclient/http.py", line 723, in execute
raise HttpError(resp, content, uri=self.uri) apiclient.errors.HttpError: <HttpError 403 when requesting https://www.googleapis.com/admin/directory/v1/users?domain=messycoders.com&alt=json returned "Not Authorized to access this resource/api">
Try:
credentials = SignedJwtAssertionCredentials(
'my#developer.gserviceaccount.com',
key,
sub='superadmin#mydomain.com',
scope=['https://www.googleapis.com/auth/admin.directory.user',])
You don't need both scopes, use readonly if you're doing read operations only, use the above if you're doing read and write.
sub= defines which Google Apps account the service account should impersonate to perform the directory operations, it's necessary and the account needs to have the right permissions.
Lastly, be sure that you've granted the service account's client_id access to the directory scopes you need in the Control Panel. The steps to do this are listed in the Drive documentation, just sub in the correct scope(s) for Admin Directory.

Python Linked API throwing error with Python 2.7

This code to fetch skills from linkedin is working fine with python 2.6
from linkedin import helper
from liclient import LinkedInAPI
import json
AUTH_TOKEN='{"oauth_token_secret": "TOKEN SECRET", "oauth_authorization_expires_in": "0", "oauth_token": "OAUTH TOKEN", "oauth_expires_in": "0"}'
consumer_key = 'CONSUMER KEY'
consumer_secret = 'CONSUMER SECRET'
APIClient =LinkedInAPI(consumer_key, consumer_secret)
request_token = APIClient.get_request_token()
field_selector_string = ['skills']
results = APIClient.get_user_profile(json.loads(AUTH_TOKEN), field_selector_string)
Skills = results[0].skills
print Skills
But when I run same code with python 2.7 gettting this error
Traceback (most recent call last):
File "linkedintest.py", line 10, in <module>
results = APIClient.get_user_profile(json.loads(AUTH_TOKEN), field_selector_string)
File "/var/www/shine/liclient/__init__.py", line 82, in get_user_profile
resp, content = client.request(url, 'GET')
File "/var/www/shine/liclient/oauth2/__init__.py", line 603, in request
req.sign_request(self.method, self.consumer, self.token)
File "/var/www/shine/liclient/oauth2/__init__.py", line 357, in sign_request
self['oauth_signature'] = signature_method.sign(self, consumer, token)
File "/var/www/shine/liclient/oauth2/__init__.py", line 683, in sign
hashed = hmac.new(key, raw, sha)
File "/usr/lib/python2.7/hmac.py", line 133, in new
return HMAC(key, msg, digestmod)
File "/usr/lib/python2.7/hmac.py", line 72, in __init__
self.outer.update(key.translate(trans_5C))
TypeError: character mapping must return integer, None or unicode
Try this:
from linkedin import helper
from liclient import LinkedInAPI
import json
AUTH_TOKEN='{"oauth_token_secret": "TOKEN SECRET", "oauth_authorization_expires_in": "0", "oauth_token": "OAUTH TOKEN", "oauth_expires_in": "0"}'
# converting unicode dict to str dict
AUTH_TOKEN=json.loads(AUTH_TOKEN)
AUTH_TOKEN_DICT = {}
for auth in AUTH_TOKEN:
AUTH_TOKEN_STR[str(auth)] = str(AUTH_TOKEN[auth])
consumer_key = 'CONSUMER KEY'
consumer_secret = 'CONSUMER SECRET'
APIClient =LinkedInAPI(consumer_key, consumer_secret)
request_token = APIClient.get_request_token()
field_selector_string = ['skills']
results = APIClient.get_user_profile(AUTH_TOKEN_DICT, field_selector_string)
Skills = results[0].skills
print Skills