Configuration is now login-protected

This commit is contained in:
Patrick McDonagh
2017-03-01 22:06:09 -06:00
parent bfbbd762c3
commit 5e4a06ddf1
9 changed files with 248 additions and 31 deletions

23
.idea/workspace.xml generated
View File

@@ -2,8 +2,12 @@
<project version="4">
<component name="ChangeListManager">
<list default="true" id="28cc251f-c94f-44ee-a66d-77aaab6e3483" name="Default" comment="">
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/www/pocwww/pocwww/templates/dashboard.jinja2" afterPath="$PROJECT_DIR$/www/pocwww/pocwww/templates/dashboard.jinja2" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/www/pocwww/pocwww/__init__.py" afterPath="$PROJECT_DIR$/www/pocwww/pocwww/__init__.py" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/www/pocwww/pocwww/json.py" afterPath="$PROJECT_DIR$/www/pocwww/pocwww/json.py" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/www/pocwww/pocwww/templates/layout.jinja2" afterPath="$PROJECT_DIR$/www/pocwww/pocwww/templates/layout.jinja2" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/www/pocwww/pocwww/view_helpers.py" afterPath="$PROJECT_DIR$/www/pocwww/pocwww/view_helpers.py" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/www/pocwww/pocwww/views.py" afterPath="$PROJECT_DIR$/www/pocwww/pocwww/views.py" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/www/pocwww/setup.py" afterPath="$PROJECT_DIR$/www/pocwww/setup.py" />
</list>
<ignored path="$PROJECT_DIR$/target/" />
<ignored path="$PROJECT_DIR$/.gradle/" />
@@ -135,7 +139,7 @@
<entry file="file://$PROJECT_DIR$/src/main/java/com/henrypump/poc/Well.java">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="467">
<caret line="1223" column="30" lean-forward="true" selection-start-line="1223" selection-start-column="30" selection-end-line="1223" selection-end-column="30" />
<caret line="1223" column="30" lean-forward="false" selection-start-line="1223" selection-start-column="30" selection-end-line="1223" selection-end-column="30" />
<folding>
<element signature="imports" expanded="true" />
</folding>
@@ -173,7 +177,7 @@
<entry file="file://$PROJECT_DIR$/src/main/java/com/henrypump/poc/WebServer.java">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="523">
<caret line="182" column="23" lean-forward="true" selection-start-line="182" selection-start-column="23" selection-end-line="182" selection-end-column="23" />
<caret line="182" column="23" lean-forward="false" selection-start-line="182" selection-start-column="23" selection-end-line="182" selection-end-column="23" />
<folding>
<element signature="imports" expanded="true" />
</folding>
@@ -772,10 +776,9 @@
<treeState />
</component>
<component name="ProjectFrameBounds">
<option name="x" value="3600" />
<option name="y" value="23" />
<option name="width" value="1920" />
<option name="height" value="1057" />
<option name="width" value="1680" />
<option name="height" value="1027" />
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
<component name="ProjectView">
@@ -1133,7 +1136,7 @@
</todo-panel>
</component>
<component name="ToolWindowManager">
<frame x="3600" y="23" width="1920" height="1057" extended-state="0" />
<frame x="0" y="23" width="1680" height="1027" extended-state="6" />
<editor active="true" />
<layout>
<window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
@@ -1150,7 +1153,7 @@
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="10" side_tool="false" content_ui="tabs" />
<window_info id="Capture Tool" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.17092651" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.1959707" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32960895" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32967034" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.24973376" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
@@ -1928,7 +1931,7 @@
<entry file="file://$PROJECT_DIR$/src/main/java/com/henrypump/poc/Well.java">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="467">
<caret line="1223" column="30" lean-forward="true" selection-start-line="1223" selection-start-column="30" selection-end-line="1223" selection-end-column="30" />
<caret line="1223" column="30" lean-forward="false" selection-start-line="1223" selection-start-column="30" selection-end-line="1223" selection-end-column="30" />
<folding>
<element signature="imports" expanded="true" />
</folding>
@@ -1938,7 +1941,7 @@
<entry file="file://$PROJECT_DIR$/src/main/java/com/henrypump/poc/WebServer.java">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="523">
<caret line="182" column="23" lean-forward="true" selection-start-line="182" selection-start-column="23" selection-end-line="182" selection-end-column="23" />
<caret line="182" column="23" lean-forward="false" selection-start-line="182" selection-start-column="23" selection-end-line="182" selection-end-column="23" />
<folding>
<element signature="imports" expanded="true" />
</folding>

View File

@@ -1,4 +1,6 @@
from pyramid.config import Configurator
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from datetime import datetime, date
from pyramid.renderers import JSON
from bson.objectid import ObjectId
@@ -74,7 +76,11 @@ def pagination_adapter(obj, request):
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(settings=settings)
authentication_policy = AuthTktAuthenticationPolicy('H3nryP7mp')
authorization_policy = ACLAuthorizationPolicy()
config = Configurator(settings=settings,
authentication_policy=authentication_policy,
authorization_policy=authorization_policy)
config.include('pyramid_jinja2')
config.commit() # this is needed or you will get None back on the next line
jinja2_env = config.get_jinja2_environment()
@@ -88,6 +94,8 @@ def main(global_config, **settings):
port=db_url.port,
)
config.registry.db.poc.users.update_one({"username": "admin"}, {"$set": {"username": "admin", "password": "l3tm31n"}}, upsert=True)
def add_db(request):
db = config.registry.db[db_url.path[1:]]
if db_url.username and db_url.password:
@@ -142,10 +150,12 @@ def main(global_config, **settings):
config.add_route('json_runstatus_page', '/json/runstatus/{page_num}')
config.add_route('json_runstatus', '/json/runstatus')
config.add_route('json_config', '/json/config')
config.add_route('config', '/config')
config.add_route('json_config', '/json/config', factory='pocwww.security.UserLoginFactory')
config.add_route('config', '/config', factory='pocwww.security.UserLoginFactory')
config.add_route('admin', '/admin')
config.add_route('admin', '/admin', factory='pocwww.security.UserLoginFactory')
config.add_route('auth', '/sign/{action}')
config.add_route('register', '/register', factory='pocwww.security.UserLoginFactory')
# JSON-ONLY ROUTES
config.add_route('json_lastcard', "/json/lastcard")
@@ -159,12 +169,15 @@ def main(global_config, **settings):
config.add_route('json_singlevaluebetween', "/json/values/tag/{tagname}")
config.add_route("json_singlevaluedaterange", "/json/values/tag/{tagname}/daterange")
config.add_route("json_updateconfig", "/json/updateconfig")
config.add_route("json_updateconfig", "/json/updateconfig", factory='pocwww.security.UserLoginFactory')
config.add_route("json_cmd_start", "/json/cmd/start")
config.add_route("json_cmd_stop", "/json/cmd/stop")
config.add_route("json_cmd_shake", "/json/cmd/shake")
config.add_route("json_update_poc_address", "/json/updatepocaddress")
config.add_route("json_cmd_start", "/json/cmd/start", factory='pocwww.security.UserLoginFactory')
config.add_route("json_cmd_stop", "/json/cmd/stop", factory='pocwww.security.UserLoginFactory')
config.add_route("json_cmd_shake", "/json/cmd/shake", factory='pocwww.security.UserLoginFactory')
config.add_route("json_update_poc_address", "/json/updatepocaddress", factory='pocwww.security.UserLoginFactory')
config.add_route("json_newuser", "/json/newuser", factory='pocwww.security.UserLoginFactory')
config.add_route("json_getusers", "/json/users", factory='pocwww.security.UserLoginFactory')
config.scan()
return config.make_wsgi_app()

View File

@@ -116,7 +116,7 @@ def json_runstatusnow(request):
return {'runstatus': status}
@view_config(route_name="json_updateconfig", renderer="prettyjson", request_method='POST')
@view_config(route_name="json_updateconfig", renderer="prettyjson", request_method='POST', permission="edit")
def json_updateconfig(request):
conv_to_float = [
'deltaT',
@@ -160,7 +160,7 @@ def json_updateconfig(request):
return {'new_config': request.json_body, 'stored_result': result, 'updated': pocCmdSts}
@view_config(route_name="json_cmd_start", renderer="prettyjson")
@view_config(route_name="json_cmd_start", renderer="prettyjson", permission="control")
def json_start(request):
address = get_poc_address(request) or 'localhost'
start_url = "http://{}:8000/command?start=true".format(address)
@@ -168,7 +168,7 @@ def json_start(request):
return r.text if r.status_code == 200 else {"status": "failure sending command"}
@view_config(route_name="json_cmd_stop", renderer="prettyjson")
@view_config(route_name="json_cmd_stop", renderer="prettyjson", permission="control")
def json_stop(request):
address = get_poc_address(request) or 'localhost'
stop_url = "http://{}:8000/command?stop=true".format(address)
@@ -176,7 +176,7 @@ def json_stop(request):
return r.text if r.status_code == 200 else {"status": "failure sending command"}
@view_config(route_name="json_cmd_shake", renderer="prettyjson")
@view_config(route_name="json_cmd_shake", renderer="prettyjson", permission="view")
def json_shake(request):
address = get_poc_address(request) or 'localhost'
shake_url = "http://{}:8000/shake".format(address)
@@ -184,7 +184,7 @@ def json_shake(request):
return r.text if r.status_code == 200 else {"status": "failure sending command"}
@view_config(route_name="json_update_poc_address", renderer="prettyjson", request_method='POST')
@view_config(route_name="json_update_poc_address", renderer="prettyjson", request_method='POST', permission="edit")
def json_update_poc_address(request):
try:
new_addr = request.json_body['pocIPAddress']
@@ -192,3 +192,32 @@ def json_update_poc_address(request):
return {"status": "OK"}
except KeyError:
return {"status": "failure"}
@view_config(route_name="json_newuser", renderer="prettyjson", request_method='POST', permission="edit")
def json_newuser(request):
jsb = request.json_body
if request.db['users'].count({"username": jsb['username']}) > 0:
fail_reason = "There is already a user with this username"
return {"status": 'fail', "info": fail_reason}
elif len(jsb['username']) < 5:
fail_reason = "The username must be at least 5 characters"
return {"status": 'fail', "info": fail_reason}
elif len(jsb['password']) < 5:
fail_reason = "The password must be at least 5 characters"
return {"status": 'fail', "info": fail_reason}
else:
set_return = set_password(request, jsb['username'], jsb['password'])
return {'status': "OK"}
@view_config(route_name="json_getusers", renderer="prettyjson", permission="edit")
def json_getuser(request):
user_list = []
users = list(request.db['users'].find())
for user in users:
user_list.append(user['username'])
return {'users': user_list}

View File

@@ -0,0 +1,10 @@
from pyramid.security import Allow, Everyone, Authenticated
class UserLoginFactory(object):
__acl__ = [(Allow, Everyone, 'view'),
(Allow, Authenticated, 'control'),
(Allow, Authenticated, 'edit'), ]
def __init__(self, request):
pass

View File

@@ -63,7 +63,23 @@
<li {% if (navgroup == 'gaugeoff') %}class="visible-xs-inline active" {% else %} class="visible-xs-inline" {% endif %}><a href="/gaugeoff">Gauge-Off</a></li>
<li {% if (navgroup == 'fluidshots') %}class="visible-xs-inline active" {% else %} class="visible-xs-inline" {% endif %}><a href="/fluidshots">Fluid Shots</a></li>
<li {% if (navgroup == 'welltests') %}class="visible-xs-inline active" {% else %} class="visible-xs-inline" {% endif %}><a href="/welltests">Well Tests</a></li>
<li><a href="/config">Configuration</a></li>
{% if request.authenticated_userid %}
<li><a href="/config">Configuration</a></li>
<li><a href="/register">{{request.authenticated_userid}}</a></li>
<li><form class="navbar-form"><div class="form-group"><a href="{{request.route_url('auth',action='out')}}" class="btn btn-warning">Sign Out</a></div></form></li>
{% else %}
<form action="{{request.route_url('auth',action='in')}}" method="post" class="form-inline navbar-form">
<div class="form-group">
<input type="text" name="username" class="form-control" placeholder="Username">
</div>
<div class="form-group">
<input type="password" name="password" class="form-control" placeholder="Password">
</div>
<div class="form-group">
<input type="submit" value="Sign in" class="btn btn-default">
</div>
</form>
{% endif %}</li>
</ul>
</div>
</div>
@@ -77,8 +93,10 @@
<h4><span id="runstatus"></span></h4>
<p>at</p>
<h5><span id="runstatustimestamp"></span></h5>
<button onclick="start()" class="btn btn-success">Start</button>
<button onclick="stop()" class="btn btn-danger">Stop</button>
{% if request.authenticated_userid %}
<button onclick="start()" class="btn btn-success">Start</button>
<button onclick="stop()" class="btn btn-danger">Stop</button>
{% endif %}
</div>
<hr />
<ul class="nav nav-sidebar">

View File

@@ -0,0 +1,103 @@
{% extends "layout.jinja2" %}
{% block content %}
{% if request.authenticated_userid %}
<h1>User Management</h1>
<hr />
<h2>New User</h2>
<form class="form-inline">
<div class="form-group">
<input type="text" name="username" class="form-control" id="username" placeholder="Username">
</div>
<div class="form-group">
<input type="password" name="password" class="form-control" id="password" placeholder="Password">
</div>
<div class="form-group">
<button id="submit" class="btn btn-default">Create User</button>
</div>
</form>
<hr />
<div class="alert alert-success alert-dismissable hidden" id="update-success">
<span>
<p>User successfully added!</p>
</span>
</div>
<div class="alert alert-danger alert-dismissable hidden" id="update-failed">
<span>
<p><span id="error-message"></span></p>
</span>
</div>
<h2>All Users</h2>
<div class="table-reponsive">
<table class="table">
<thead>
<tr>
<th>Username</th>
<th></th>
</thead>
<tbody>
</tbody>
</table>
</div>
<script>
function addedUser(data){
if (data.status == "OK"){
$('#update-success').removeClass('hidden');
} else {
$('#update-failed').removeClass('hidden');
$('#error-message').text(data.info);
}
};
function sendNewUser(){
var newUser = {
username: $("#username").val(),
password: $("#password").val()
};
$.ajax({
type: "POST",
dataType: 'json',
data: JSON.stringify(newUser),
contentType: "application/json; charset=utf-8",
url: "/json/newuser",
success: addedUser
});
};
$("#submit").click(function(event){
event.preventDefault();
$('#update-success').addClass('hidden');
$('#update-failed').addClass('hidden');
sendNewUser();
});
function showAllUsers(data){
$('tbody').empty();
for(var i = 0; i < data.users.length; i++){
$('tbody').append("<tr><td>" + data.users[i] + '</td><td><button class="btn btn-danger">Delete</button></td></tr>');
}
}
function getAllUsers(){
$.ajax({
dataType: 'json',
url:"/json/users",
success: showAllUsers
});
}
getAllUsers();
</script>
{% endif %}
{% endblock content %}

View File

@@ -1,5 +1,6 @@
from datetime import datetime, timedelta
from math import ceil
from passlib.apps import custom_app_context as poc_pwd_context
from .pagination import Pagination
@@ -83,3 +84,21 @@ def get_poc_address(request):
if len(addr_obj) > 0:
address = addr_obj[0]['pocIPAddress']
return address
def set_password(request, username, password):
password_hash = poc_pwd_context.encrypt(password)
request.db['users'].update_one({'username': username}, {"$set":{"username": username, "password": password_hash}}, upsert=True)
def check_password(request, username, password):
users = list(request.db['users'].find({'username': username}))
if len(users) > 0:
this_user = users[0]
# is it cleartext?
if password == this_user['password']:
set_password(request, username, password)
return check_password(request, username, password)
return poc_pwd_context.verify(password, this_user['password'])

View File

@@ -1,4 +1,6 @@
from pyramid.view import view_config
from pyramid.httpexceptions import HTTPFound
from pyramid.security import remember, forget
from datetime import datetime, timedelta
from math import ceil
from .view_helpers import *
@@ -134,14 +136,33 @@ def run_status(request):
return {'navgroup': 'runstatus', 'data': runStatuses, 'pagination': Pagination(page_num, num_per_page, num_cards)}
@view_config(route_name="config", renderer="templates/config.jinja2")
@view_config(route_name="json_config", renderer="prettyjson")
@view_config(route_name="config", renderer="templates/config.jinja2", permission="edit")
@view_config(route_name="json_config", renderer="prettyjson", permission="edit")
def well_config(request):
current_configuration = list(request.db['wellConfiguration'].find().sort("timestamp", -1).limit(1))[0]
return {'navgroup': 'config', 'config': current_configuration}
@view_config(route_name="admin", renderer="templates/admin.jinja2")
@view_config(route_name="admin", renderer="templates/admin.jinja2", permission="edit")
def admin_view(request):
address = get_poc_address(request) or 'localhost'
return {'navgroup': 'admin', 'pocIPAddress': address}
@view_config(route_name="auth")
def sign_in_out(request):
username = request.POST.get('username')
if username:
if check_password(request, username, request.POST.get('password')):
headers = remember(request, username)
else:
headers = forget(request)
else:
headers = forget(request)
return HTTPFound(location=request.route_url('home'), headers=headers)
@view_config(route_name='register',
renderer='templates/register.jinja2', permission="edit")
def register(request):
return {"navgroup": "user"}

View File

@@ -14,7 +14,8 @@ requires = [
'pyramid_debugtoolbar',
'waitress',
'pymongo',
'requests'
'requests',
'passlib'
]
tests_require = [