def test_logins_valid_user(self): user = user_factory.create(email='*****@*****.**') with patch.object( settings, 'GOOGLE_APPS_DOMAIN', 'example.com'), patch( 'redash.authentication.login_user') as login_user_mock: create_and_login_user(None, user) login_user_mock.assert_called_once_with(user, remember=True)
def test_ignores_invliad_user(self): user = ObjectDict({'email': '*****@*****.**'}) with patch.object( settings, 'GOOGLE_APPS_DOMAIN', 'example.com'), patch( 'redash.authentication.login_user') as login_user_mock: create_and_login_user(None, user) self.assertFalse(login_user_mock.called)
def test_creates_vaild_new_user(self): openid_user = ObjectDict({'email': '*****@*****.**', 'name': 'Test User'}) with patch.multiple(settings, GOOGLE_APPS_DOMAIN='example.com'), \ patch('redash.authentication.login_user') as login_user_mock: create_and_login_user(None, openid_user) self.assertTrue(login_user_mock.called) user = models.User.get(models.User.email == openid_user.email)
def test_creates_vaild_new_user(self): email = "*****@*****.**" name = "Test User" with patch("redash.authentication.login_user") as login_user_mock: create_and_login_user(self.factory.org, name, email) self.assertTrue(login_user_mock.called) user = models.User.query.filter(models.User.email == email).one() self.assertEqual(user.email, email)
def idp_initiated(org_slug=None): if not current_org.get_setting("auth_saml_enabled"): logger.error("SAML Login is not enabled") return redirect(url_for('redash.index', org_slug=org_slug)) saml_client = get_saml_client(current_org) try: authn_response = saml_client.parse_authn_request_response( request.form['SAMLResponse'], entity.BINDING_HTTP_POST) except Exception: logger.error('Failed to parse SAML response', exc_info=True) flash('SAML login failed. Please try again later.') return redirect(url_for('redash.login', org_slug=org_slug)) authn_response.get_identity() user_info = authn_response.get_subject() email = user_info.text name = "%s %s" % (authn_response.ava['FirstName'][0], authn_response.ava['LastName'][0]) # This is what as known as "Just In Time (JIT) provisioning". # What that means is that, if a user in a SAML assertion # isn't in the user store, we create that user first, then log them in user = create_and_login_user(current_org, name, email) if user is None: return logout_and_redirect_to_index() if 'RedashGroups' in authn_response.ava: group_names = authn_response.ava.get('RedashGroups') user.update_group_assignments(group_names) url = url_for('redash.index', org_slug=org_slug) return redirect(url)
def login(org_slug=None): unsafe_next_path = request.args.get('next') next_path = get_next_path(unsafe_next_path) if not settings.REMOTE_USER_LOGIN_ENABLED: logger.error("Cannot use remote user for login without being enabled in settings") return redirect(url_for('redash.index', next=next_path, org_slug=org_slug)) email = request.headers.get(settings.REMOTE_USER_HEADER) # Some Apache auth configurations will, stupidly, set (null) instead of a # falsey value. Special case that here so it Just Works for more installs. # '(null)' should never really be a value that anyone wants to legitimately # use as a redash user email. if email == '(null)': email = None if not email: logger.error("Cannot use remote user for login when it's not provided in the request (looked in headers['" + settings.REMOTE_USER_HEADER + "'])") return redirect(url_for('redash.index', next=next_path, org_slug=org_slug)) logger.info("Logging in " + email + " via remote user") user = create_and_login_user(current_org, email, email) if user is None: return logout_and_redirect_to_index() return redirect(next_path or url_for('redash.index', org_slug=org_slug), code=302)
def authorized(): app = xinniuren_remote_app() resp = app.authorized_response() access_token = resp['access_token'] if access_token is None: logger.warning("Access token missing in call back request.") flash("Validation error. Please retry.") return redirect(url_for('redash.login')) profile = get_user_profile(access_token) if profile is None: flash("Validation error. Please retry.") return redirect(url_for('redash.login')) if 'org_slug' in session: org = models.Organization.get_by_slug(session.pop('org_slug')) else: org = current_org if not verify_profile(org, profile): logger.warning("User tried to login with unauthorized domain name: %s (org: %s)", profile['Email'], org) flash("Your Google Apps account ({}) isn't allowed.".format(profile['Email'])) return redirect(url_for('redash.login', org_slug=org.slug)) # picture_url = "%s?sz=40" % profile['picture'] picture_url = "/static/images/avatar.svg" user = create_and_login_user(org, profile['UserName'], profile['Email'], picture_url) if user is None: return logout_and_redirect_to_index() unsafe_next_path = request.args.get('state') or url_for("redash.index", org_slug=org.slug) next_path = get_next_path(unsafe_next_path) return redirect(next_path)
def login(org_slug=None): unsafe_next_path = request.args.get('next') next_path = get_next_path(unsafe_next_path) if not settings.REMOTE_USER_LOGIN_ENABLED: logger.error( "Cannot use remote user for login without being enabled in settings" ) return redirect( url_for('redash.index', next=next_path, org_slug=org_slug)) email = request.headers.get(settings.REMOTE_USER_HEADER) # Some Apache auth configurations will, stupidly, set (null) instead of a # falsey value. Special case that here so it Just Works for more installs. # '(null)' should never really be a value that anyone wants to legitimately # use as a redash user email. if email == '(null)': email = None if not email: logger.error( "Cannot use remote user for login when it's not provided in the request (looked in headers['" + settings.REMOTE_USER_HEADER + "'])") return redirect( url_for('redash.index', next=next_path, org_slug=org_slug)) logger.info("Logging in " + email + " via remote user") user = create_and_login_user(current_org, email, email) if user is None: return logout_and_redirect_to_index() return redirect(next_path or url_for('redash.index', org_slug=org_slug), code=302)
def authorized(): resp = google_remote_app().authorized_response() access_token = resp['access_token'] if access_token is None: logger.warning("Access token missing in call back request.") flash("Validation error. Please retry.") return redirect(url_for('redash.login')) profile = get_user_profile(access_token) if profile is None: flash("Validation error. Please retry.") return redirect(url_for('redash.login')) if 'org_slug' in session: org = models.Organization.get_by_slug(session.pop('org_slug')) else: org = current_org if not verify_profile(org, profile): logger.warning("User tried to login with unauthorized domain name: %s (org: %s)", profile['email'], org) flash("Your Google Apps account ({}) isn't allowed.".format(profile['email'])) return redirect(url_for('redash.login', org_slug=org.slug)) picture_url = "%s?sz=40" % profile['picture'] user = create_and_login_user(org, profile['name'], profile['email'], picture_url) if user is None: return logout_and_redirect_to_index() next_path = request.args.get('state') or url_for("redash.index", org_slug=org.slug) return redirect(next_path)
def login(org_slug=None): index_url = url_for("redash.index", org_slug=org_slug) next_path = request.args.get('next', index_url) if not settings.LDAP_LOGIN_ENABLED: logger.error("Cannot use LDAP for login without being enabled in settings") return redirect(url_for('redash.index', next=next_path)) if current_user.is_authenticated: return redirect(next_path) if request.method == 'POST': ldap_user = auth_ldap_user(request.form['email'], request.form['password']) if ldap_user is not None: user = create_and_login_user( current_org, ldap_user[settings.LDAP_DISPLAY_NAME_KEY][0], ldap_user[settings.LDAP_EMAIL_KEY][0] ) if user is None: return logout_and_redirect_to_index() return redirect(next_path or url_for('redash.index')) else: flash("Incorrect credentials.") return render_template("login.html", org_slug=org_slug, next=next_path, email=request.form.get('email', ''), show_password_login=True, username_prompt=settings.LDAP_CUSTOM_USERNAME_PROMPT, hide_forgot_password=True)
def login(org_slug=None): next_path = request.args.get('next') if not settings.REMOTE_JWT_LOGIN_ENABLED: logger.error( "Cannot use remote user for login without being enabled in settings" ) return redirect( url_for('redash.index', next=next_path, org_slug=org_slug)) jwttoken = request.headers.get( settings.REMOTE_USER_HEADER) or request.cookies.get('jwt') # Some Apache auth configurations will, stupidly, set (null) instead of a # falsey value. Special case that here so it Just Works for more installs. # '(null)' should never really be a value that anyone wants to legitimately # use as a redash user jwt. if jwttoken == '(null)': jwttoken = None if not jwt: logger.error( "Cannot use remote jwt for login when it's not provided in the request (looked in headers['" + settings.REMOTE_USER_HEADER + "'])") return redirect( url_for('redash.index', next=next_path, org_slug=org_slug)) try: public_key = get_jwt_public_key() jwt_decoded = jwt.get_unverified_claims( jwttoken) if public_key is '' else jwt.decode( jwttoken, public_key) email = jwt_decoded.get('email', None) if not email: logger.error( "Cannot use remote jwt for login when it's not provided in the request (looked in headers['" + settings.REMOTE_USER_HEADER + "'])") return redirect( url_for('redash.index', next=next_path, org_slug=org_slug)) logger.info("Logging in " + email + " via remote jwt") user = create_and_login_user(current_org, email, email) if user is None: return logout_and_redirect_to_index() resp = redirect(next_path or url_for('redash.index', org_slug=org_slug), code=302) resp.set_cookie('jwt', jwttoken, secure=True, httponly=True) return resp except jwt.JWTError, jwt.ExpiredSignatureError: logger.error( "Remote user attempted entry using key with invalid signature") logger.info(settings.REMOTE_JWT_EXPIRED_ENDPOINT) return redirect( url_for('redash.index', next=next_path, org_slug=org_slug))
def app_profile(): resp = register_remote_app().get('oauth-2/resource') profile = resp.json() picture_url = "%s?sz=40" % profile['picture'] user = create_and_login_user(current_org, profile['name'], profile['email'], picture_url) if user is None: return logout_and_redirect_to_index() # next_path = request.args.get('state') or url_for("redash.index", org_slug=org.slug) next_path = request.args.get('state') or url_for("redash.index") return redirect(next_path)
def authorized(): logger.debug("Authorized user inbound") resp = oauth.google.authorize_access_token() user = resp.get("userinfo") if user: session["user"] = user access_token = resp["access_token"] if access_token is None: logger.warning("Access token missing in call back request.") flash("Validation error. Please retry.") return redirect(url_for("redash.login")) profile = get_user_profile(access_token) if profile is None: flash("Validation error. Please retry.") return redirect(url_for("redash.login")) if "org_slug" in session: org = models.Organization.get_by_slug(session.pop("org_slug")) else: org = current_org if not verify_profile(org, profile): logger.warning( "User tried to login with unauthorized domain name: %s (org: %s)", profile["email"], org, ) flash("Your Google Apps account ({}) isn't allowed.".format( profile["email"])) return redirect(url_for("redash.login", org_slug=org.slug)) picture_url = "%s?sz=40" % profile["picture"] user = create_and_login_user(org, profile["name"], profile["email"], picture_url) if user is None: return logout_and_redirect_to_index() unsafe_next_path = session.get("next_url") or url_for( "redash.index", org_slug=org.slug) next_path = get_next_path(unsafe_next_path) return redirect(next_path)
def login(org_slug=None): next_path = request.args.get('next') if not settings.REMOTE_USER_LOGIN_ENABLED: logger.error( "Cannot use remote user for login without being enabled in settings" ) return redirect( url_for('redash.index', next=next_path, org_slug=org_slug)) email = request.headers.get(settings.REMOTE_USER_HEADER) # Some Apache auth configurations will, stupidly, set (null) instead of a # falsey value. Special case that here so it Just Works for more installs. # '(null)' should never really be a value that anyone wants to legitimately # use as a redash user email. if email == '(null)': email = None if not email: logger.error( "Cannot use remote user for login when it's not provided in the request (looked in headers['" + settings.REMOTE_USER_HEADER + "'])") return redirect( url_for('redash.index', next=next_path, org_slug=org_slug)) # Check if there is a header of user groups and if yes # check it against a list of allowed user groups from the settings if settings.REMOTE_GROUPS_ENABLED: remote_groups = settings.set_from_string( request.headers.get(settings.REMOTE_GROUPS_HEADER) or '') allowed_groups = settings.REMOTE_GROUPS_ALLOWED if not allowed_groups.intersection(remote_groups): logger.error( "User groups provided in the %s header are not " "matching the allowed groups.", settings.REMOTE_GROUPS_HEADER) return redirect(url_for('redash.index', next=next_path)) logger.info("Logging in " + email + " via remote user") user = create_and_login_user(current_org, email, email) if user is None: return logout_and_redirect_to_index() return redirect(next_path or url_for('redash.index', org_slug=org_slug), code=302)
def idp_initiated(org_slug=None): if not current_org.get_setting("auth_saml_enabled"): logger.error("SAML Login is not enabled") return redirect(url_for("redash.index", org_slug=org_slug)) saml_client = get_saml_client(current_org) try: authn_response = saml_client.parse_authn_request_response( request.form["SAMLResponse"], entity.BINDING_HTTP_POST ) except Exception: logger.error("Failed to parse SAML response", exc_info=True) flash("SAML login failed. Please try again later.") return redirect(url_for("redash.login", org_slug=org_slug)) authn_response.get_identity() user_info = authn_response.get_subject() email = user_info.text # Google SAML auth does not return these FirstName / LastName fields try: name = "%s %s" % (authn_response.ava['FirstName'][0], authn_response.ava['LastName'][0]) except Exception as e: # logger.exception(e) logger.warning("could not fetch FirstName or LastName from SAML response: falling back to email user") name = email.split('@')[0] # This is what as known as "Just In Time (JIT) provisioning". # What that means is that, if a user in a SAML assertion # isn't in the user store, we create that user first, then log them in user = create_and_login_user(current_org, name, email) if user is None: return logout_and_redirect_to_index() if "RedashGroups" in authn_response.ava: group_names = authn_response.ava.get("RedashGroups") user.update_group_assignments(group_names) url = url_for("redash.index", org_slug=org_slug) return redirect(url)
def authorized(): code = request.args.get('code') if code is None: logger.warning("code missing in call back request.") flash("Validation error. Please retry.") return redirect(url_for("redash.login")) profile = get_user_profile(code) if profile is None: flash("Validation error. Please retry.") return redirect(url_for("redash.login")) if "org_slug" in session: org = models.Organization.get_by_slug(session.pop("org_slug")) else: org = current_org if not verify_profile(org, profile): logger.warning( "User tried to login with unauthorized domain name: %s (org: %s)", profile["email"], org, ) flash("Your microsoft Apps account ({}) isn't allowed.".format( profile["email"])) return redirect(url_for("redash.login", org_slug=org.slug)) picture_url = profile.get('picture') user = create_and_login_user(org, profile["name"], profile["email"], picture_url) if user is None: return logout_and_redirect_to_index() unsafe_next_path = request.args.get("state") or url_for("redash.index", org_slug=org.slug) next_path = get_next_path(unsafe_next_path) return redirect(next_path)
def authorized(): resp = google_remote_app().authorized_response() access_token = resp['access_token'] if access_token is None: logger.warning("Access token missing in call back request.") flash("Validation error. Please retry.") return redirect(url_for('redash.login')) profile = get_user_profile(access_token) if profile is None: flash("Validation error. Please retry.") return redirect(url_for('redash.login')) if 'org_slug' in session: org = models.Organization.get_by_slug(session.pop('org_slug')) else: org = current_org if not verify_profile(org, profile): logger.warning( "User tried to login with unauthorized domain name: %s (org: %s)", profile['email'], org) session['relogin'] = '******' return redirect(url_for('redash.login', org_slug=org.slug)) picture_url = "%s?sz=40" % profile['picture'] user = create_and_login_user(org, profile['name'], profile['email'], picture_url) if user is None: return logout_and_redirect_to_index() next_path = request.args.get('state') or url_for("redash.index", org_slug=org.slug) return redirect(next_path)
def test_ignores_invliad_user(self): user = ObjectDict({'email': '*****@*****.**'}) with patch.object(settings, 'GOOGLE_APPS_DOMAIN', 'example.com'), patch('redash.authentication.login_user') as login_user_mock: create_and_login_user(None, user) self.assertFalse(login_user_mock.called)
def test_logins_valid_user(self): user = user_factory.create(email='*****@*****.**') with patch.object(settings, 'GOOGLE_APPS_DOMAIN', 'example.com'), patch('redash.authentication.login_user') as login_user_mock: create_and_login_user(None, user) login_user_mock.assert_called_once_with(user, remember=True)
def test_updates_user_name(self): user = self.factory.create_user(email="*****@*****.**") with patch("redash.authentication.login_user") as login_user_mock: create_and_login_user(self.factory.org, "New Name", user.email) login_user_mock.assert_called_once_with(user, remember=True)