Skip to content
Snippets Groups Projects
Commit ebf1278f authored by Mart van Santen's avatar Mart van Santen
Browse files

Merge branch 'loginpanel' into 104-allow-cli-to-set-password-create-inital-admin-user

parents 62859458 d8e65de4
No related branches found
No related tags found
2 merge requests!68Merge loginpanel into main and release 0.5.0,!60Resolve "Allow CLI to set password"
"""Basic setup for behave and chromedriver."""
import behave_webdriver
import os
import re
import time
from behave_webdriver.driver import ChromeOptions
def save_screenshot(context, step):
"""Save a screenshot to ./screenshots."""
timestamp = time.strftime("%Y-%m-%dT%H:%M:%S")
filename = re.sub('\W', '-', '{} failed {}'.format(timestamp,
str(step.name)))
filepath = os.path.join('screenshots', filename + '.png')
if not os.path.exists('screenshots'):
os.mkdir('screenshots')
print('Saving screenshot to %s' % filepath)
context.behave_driver.save_screenshot(filepath)
def before_all(context):
"""Run at the very beginning."""
userdata = context.config.userdata
headless = userdata.get('headless', 'False')
chrome_options = ChromeOptions()
if headless == 'True':
chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
context.behave_driver = behave_webdriver.Chrome(
chrome_options=chrome_options)
def before_tag(context, tag):
values = dict()
userdata = context.config.userdata
values['home'] = userdata.get('url')
values['logout'] = values['home'] + "/logout"
values['userinfo'] = values['home'] + "/userinfo"
values['single-sign-off'] = values['home'] + "/single-sign-off"
values['username'] = userdata.get('username')
values['username2'] = userdata.get('username2')
values['password'] = userdata.get('password')
values['email'] = userdata.get('email')
values['role'] = userdata.get('role')
assert values['home'], 'url variable missing in' \
'userdata. Provide it with "-D url".'
assert values['username'], 'username variable missing in' \
'userdata. Provide it with "-D username".'
assert values['password'], 'password variable missing in' \
'userdata. Provide it with "-D password".'
assert values['email'], 'email variable missing in' \
'userdata. Provide it with "-D email".'
assert values['role'], 'role variable missing in' \
'userdata. Provide it with "-D role".'
context.oauth = values
def after_all(context):
"""Cleanup after tests run."""
context.behave_driver.quit()
def after_step(context, step):
"""Save screeshot if step fails."""
if step.status == 'failed':
save_screenshot(context, step)
print("Console log output:")
for message in context.behave_driver.get_log('browser'):
print(" Level: {}".format(message['level']))
print(" Timestamp: {}".format(message['timestamp']))
print(" Source: {}".format(message['source']))
print(" Message: {}\n".format(message['message']))
@oauth
Feature: Test login-provider function
As a Stackspin user
I want to be able to login to a Stackspin App
And verify my userdata that is provided by OpenID Connect
Scenario: Open the oAuth application and Login witha valid user
Given the oauth client "home" URL was opened
And the element "input#username" is visible
When I enter the "username" in the inputfield "input#username"
And I enter the "password" in the inputfield "input#password"
And I click on the button "input#submit"
Then I wait on element "input#password" for 1000ms to not exist
And I expect that the path is "/"
And I expect that element "body" contains the text "access_token"
Scenario: Get OpenID Connect userdata for testuser
When I open the oauth client "userinfo" URL
Then I expect that the "preferred_username" in the json output is the same as oauth variable "username"
And I expect that the "email" in the json output is the same as oauth variable "email"
And I expect that the "stackspin_roles" in the json output contains the value of oauth variable "role"
And I expect that the "name" in the json output is the same as oauth variable "username"
Scenario: Logout
When I open the oauth client "logout" URL
Then I wait on element "input#username" for 1000ms to be visible
And I expect that element "input#password" is visible
And I expect that element "input#submit" is visible
@oauth
Feature: Test logout-provider function
As a Stackspin user
I want to be able to use single-sign off triggered by a
Stackspin APP. And verify that even though I selected remember me
my session was removed.
Scenario: Login with a valid user and remember session
Given the oauth client "home" URL was opened
And the element "input#remember" is visible
When I enter the "username" in the inputfield "input#username"
And I enter the "password" in the inputfield "input#password"
And I click on the element "input#remember"
And I click on the button "input#submit"
Then I wait on element "input#password" for 1000ms to not exist
Scenario: Logout using the single sign-off feature
Given the oauth client "single-sign-off" URL was opened
And I pause for 1000ms
Then I wait on element "input#username" for 1000ms to exist
@oauth
Feature: Test features that prohibit unauthorized access
As an attacker or unauthorized user
I want to to login to a Stackspin App
And the single sign-on will block my login attempts
Scenario: Login with a valid user without access to an application
Given the oauth client "home" URL was opened
And the element "input#username" is visible
When I enter the "username2" in the inputfield "input#username"
And I enter the "password" in the inputfield "input#password"
And I click on the button "input#submit"
Then I wait on element "input#password" for 1000ms to not exist
And I expect that element "input#username" does not exist
And I expect that element "body" contains the text "error"
And I expect that element "body" contains the text "Permission denied"
And I expect that element "body" contains the text "missing application permission"
Scenario: Login with an invalid user
Given the oauth client "home" URL was opened
And the element "input#username" is visible
When I set "not_a_valid_user" to the inputfield "input#username"
And I set "password" to the inputfield "input#password"
And I click on the button "input#submit"
Then I wait on element "input#password" for 1000ms to not exist
And I expect that element "input#username" does not exist
And I expect that element "body" contains the text "error"
And I expect that element "body" contains the text "Login denied"
And I expect that element "body" contains the text "Invalid username or password"
@oauth
Feature: Testing single sign-on sessions
As a Stackspin user
I want to login once to use an application
And I use my active single sign-on session to login again without providing credentials
Scenario: Login with a valid user and remember session
Given the oauth client "home" URL was opened
And the element "input#username" is visible
And the element "input#remember" is visible
When I enter the "username" in the inputfield "input#username"
And I enter the "password" in the inputfield "input#password"
And I click on the element "input#remember"
And I click on the button "input#submit"
Then I wait on element "input#password" for 1000ms to not exist
And I expect that element "input#username" does not exist
And I expect that the path is "/"
And I expect that element "body" contains the text "access_token"
Scenario: Login without providing credentials
Given the oauth client "logout" URL was opened
And I pause for 1000ms
And there is no element "input#username" on the page
And there is no element "input#password" on the page
And the element "button#continue" is visible
When I click on the element "button#continue"
Then I wait on element "button#continue" for 1000ms to not exist
And I expect that the path is "/"
And I expect that element "body" contains the text "access_token"
Scenario: Terminate single sign-on session
Given the oauth client "logout" URL was opened
And I pause for 1000ms
And the element "button#logout" is visible
When I click on the element "button#logout"
Then I expect that the "error" in the json output is "Login cancelled"
And I expect that the "error_description" in the json output is "Login was cancelled and user session was terminated"
"""Custom steps for tests that anaylize a website that returns a json object."""
import json
from behave import given, when, then
from behave_webdriver.steps import *
@then(u'I expect that the "{variable}" in the json output is the same as oauth variable "{value}"')
def step_impl(context, variable, value):
assert context.oauth[value] == get_value_from_json_body(context, variable)
@then(u'I expect that the "{variable}" in the json output is "{value}"')
def step_impl(context, variable, value):
assert value == get_value_from_json_body(context, variable)
@then(u'I expect that the "{variable}" in the json output contains the value of oauth variable "{value}"')
def step_impl(context, variable, value):
assert context.oauth[value] in get_value_from_json_body(context, variable)
@then(u'I expect that the "{variable}" in the json output contains "{value}"')
def step_impl(context, variable, value):
assert value in get_value_from_json_body(context, variable)
def get_value_from_json_body(context, key):
obj_serialized = context.behave_driver.get_element("body").text
return json.loads(obj_serialized)[key]
"""Custom steps for login tests that use oauth environment variables"""
from behave import given, when, then
from behave_webdriver.steps import *
def before_all(context):
pass # login and save cookies here
@when(u'I open the oauth client "{url}" URL')
@given(u'The oauth client "{url}" URL was opened')
def step_impl(context, url):
context.behave_driver.get(context.oauth[url])
@when(u'I enter the "{attribute}" in the inputfield "{element}"')
def step_impl(context, attribute, element):
"""Enter value into login inputfields."""
elem = context.behave_driver.get_element(element)
elem.clear()
value = context.oauth[attribute]
elem.send_keys(value)
@then(u'I expect that element "{element}" contains the value of var "{variable}"')
def step_impl(context, element, variable):
"""Check if value is in field"""
elem = context.behave_driver.get_element(element)
value = context.oauth[variable]
assert value in elem.text
......@@ -5,5 +5,6 @@ COPY requirements.txt /
RUN apk add --no-cache \
build-base=0.5-r2 \
git=2.34.1-r0 \
libffi-dev \
bc=1.07.1-r1 && \
pip3 install --no-cache-dir -r requirements.txt
......@@ -2,3 +2,4 @@ darker
pylint
pylint-flask-sqlalchemy
pylint-flask
oic
"""
Stub app to check if SSO works in testing
"""
import json
from os import environ
from flask import Flask, url_for, redirect, jsonify, session, request
......@@ -32,6 +36,8 @@ sso.store_registration_info(sso_client_reg)
@app.route("/")
def index():
"""Index"""
if "state" not in session.keys():
state = rndstr()
session["state"] = state
......@@ -46,6 +52,7 @@ def index():
@app.route("/userinfo")
def info():
"""Show info"""
if "state" in session.keys() and sso.grant[session["state"]].is_valid:
token = sso.grant[session["state"]].get_token()
return jsonify(token.id_token.to_dict())
......@@ -53,12 +60,14 @@ def info():
@app.route("/logout")
def logout():
"""Logout"""
del sso.grant[session["state"]]
del session["state"]
return redirect("/")
@app.route("/single-sign-off")
def single_sign_off():
"""Single Sign Off Call"""
redir = sso.construct_CheckSessionRequest(state=session["state"])
answer = sso.do_end_session_request(state=session["state"], request_args={
"id_token_hint": redir["id_token"],
......@@ -69,6 +78,7 @@ def single_sign_off():
@app.route("/callback")
def callback():
"""Callback"""
if "error" in request.args:
return jsonify(request.args)
aresp = sso.parse_response(AuthorizationResponse, info=json.dumps(request.args))
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment