Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Simone Rossi
main
Commits
3e25ff90
Commit
3e25ff90
authored
Sep 18, 2018
by
Daniele Venzano
Browse files
Implementation of admin pages, first part
parent
9769a171
Changes
9
Hide whitespace changes
Inline
Side-by-side
zoe_api/api_endpoint.py
View file @
3e25ff90
...
...
@@ -283,20 +283,19 @@ class APIEndpoint:
if
'auth_source'
in
user_data
:
update_fields
[
'auth_source'
]
=
user_data
[
'auth_source'
]
if
user_data
[
'auth_source'
]
!=
'internal'
:
u
ser_data
[
'password'
]
=
None
u
pdate_fields
[
'password'
]
=
None
if
'quota_id'
in
user_data
:
quota
=
self
.
quota_by_id
(
user_data
[
'quota_id'
])
if
quota
is
None
:
raise
zoe_api
.
exceptions
.
ZoeRestAPIException
(
'No quota with ID {}'
.
format
(
user_data
[
'quota_id'
]))
update_fields
[
'quota_id'
]
=
quota
.
id
if
'role'
in
user_data
:
if
'role
_id
'
in
user_data
:
role
=
self
.
role_by_id
(
user_data
[
'role_id'
])
if
role
is
None
:
raise
zoe_api
.
exceptions
.
ZoeRestAPIException
(
'No role with ID {}'
.
format
(
user_data
[
'role_id'
]))
update_fields
[
'role_id'
]
=
role
.
id
if
'password'
in
user_data
:
update_fields
[
'password'
]
=
user_data
[
'password'
]
update_fields
[
'auth_source'
]
=
'internal'
if
'fs_uid'
in
user_data
:
update_fields
[
'fs_uid'
]
=
user_data
[
'fs_uid'
]
...
...
zoe_api/web/__init__.py
View file @
3e25ff90
...
...
@@ -24,6 +24,7 @@ import zoe_api.web.websockets
import
zoe_api.web.executions
import
zoe_api.web.zapp_shop
import
zoe_api.web.status
import
zoe_api.web.admin
import
zoe_lib.config
from
zoe_lib.version
import
ZOE_API_VERSION
,
ZOE_VERSION
...
...
@@ -56,7 +57,11 @@ def web_init(api_endpoint) -> List[tornado.web.URLSpec]:
tornado
.
web
.
url
(
base_path
+
r
'/zapp-shop/logo/([0-9a-z_\-.]+)'
,
zoe_api
.
web
.
zapp_shop
.
ZAppLogoWeb
,
route_args
,
name
=
'zappshop_logo'
),
tornado
.
web
.
url
(
base_path
+
r
'/zapp-shop/start/([0-9a-z_\-.]+)'
,
zoe_api
.
web
.
zapp_shop
.
ZAppStartWeb
,
route_args
,
name
=
'zappshop_start'
),
tornado
.
web
.
url
(
base_path
+
r
'/status'
,
zoe_api
.
web
.
status
.
StatusEndpointWeb
,
route_args
,
name
=
'status'
)
tornado
.
web
.
url
(
base_path
+
r
'/status'
,
zoe_api
.
web
.
status
.
StatusEndpointWeb
,
route_args
,
name
=
'status'
),
tornado
.
web
.
url
(
base_path
+
r
'/admin/users'
,
zoe_api
.
web
.
admin
.
UsersEndpointWeb
,
route_args
,
name
=
'admin_users'
),
tornado
.
web
.
url
(
base_path
+
r
'/admin/roles'
,
zoe_api
.
web
.
admin
.
RolesEndpointWeb
,
route_args
,
name
=
'admin_roles'
),
tornado
.
web
.
url
(
base_path
+
r
'/admin/quotas'
,
zoe_api
.
web
.
admin
.
QuotasEndpointWeb
,
route_args
,
name
=
'admin_quotas'
)
]
return
web_routes
...
...
zoe_api/web/admin.py
0 → 100644
View file @
3e25ff90
# Copyright (c) 2018, Daniele Venzano
#
# 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.
"""Main points of entry for the Zoe web interface."""
from
zoe_api.web.request_handler
import
ZoeWebRequestHandler
class
UsersEndpointWeb
(
ZoeWebRequestHandler
):
"""Handler class"""
def
get
(
self
):
"""User admin page."""
if
self
.
current_user
is
None
or
not
self
.
current_user
.
role
.
can_change_config
:
return
template_vars
=
{
'kind'
:
'users'
,
'column_keys'
:
[
'username'
,
'email'
,
'enabled'
,
'priority'
,
'fs_uid'
,
'auth_source'
,
'role'
,
'quota'
],
'column_names'
:
[
'Username'
,
'Email'
,
'Enabled'
,
'Priority'
,
'FS UID'
,
'Authentication'
,
'Role'
,
'Quota'
],
'column_types'
:
{
'username'
:
None
,
'email'
:
'text'
,
'enabled'
:
'bool'
,
'priority'
:
'number'
,
'fs_uid'
:
'number'
,
'auth_source'
:
'list'
,
'role'
:
'list'
,
'quota'
:
'list'
},
'lists'
:
{
'auth_source'
:
[
'internal'
,
'pam'
,
'ldap'
,
'ldap+sasl'
,
'gitlab-eurecom'
],
'role'
:
self
.
api_endpoint
.
role_list
(
self
.
current_user
),
'quota'
:
self
.
api_endpoint
.
quota_list
(
self
.
current_user
)
},
'rows'
:
self
.
api_endpoint
.
user_list
(
self
.
current_user
)
}
self
.
render
(
'admin.jinja2'
,
**
template_vars
)
def
post
(
self
):
"""Form submitted."""
if
self
.
current_user
is
None
or
not
self
.
current_user
.
role
.
can_change_config
:
return
user_id
=
int
(
self
.
get_argument
(
'id'
))
if
self
.
get_argument
(
'action'
)
==
'update'
:
user_data
=
{
'email'
:
None
if
self
.
get_argument
(
'email'
)
==
''
or
self
.
get_argument
(
'email'
)
==
'None'
else
self
.
get_argument
(
'email'
),
'enabled'
:
True
if
self
.
get_argument
(
'enabled'
)
==
'on'
else
False
,
'fs_uid'
:
int
(
self
.
get_argument
(
'fs_uid'
)),
'auth_source'
:
self
.
get_argument
(
'auth_source'
),
'role_id'
:
self
.
api_endpoint
.
role_by_name
(
self
.
get_argument
(
'role'
)).
id
,
'quota_id'
:
self
.
api_endpoint
.
quota_by_name
(
self
.
get_argument
(
'quota'
)).
id
}
self
.
api_endpoint
.
user_update
(
self
.
current_user
,
user_id
,
user_data
)
elif
self
.
get_argument
(
'action'
)
==
'delete'
:
self
.
api_endpoint
.
user_delete
(
self
.
current_user
,
user_id
)
self
.
redirect
(
self
.
reverse_url
(
'admin_users'
))
class
RolesEndpointWeb
(
ZoeWebRequestHandler
):
"""Handler class"""
def
get
(
self
):
"""Roles admin page"""
if
self
.
current_user
is
None
or
not
self
.
current_user
.
role
.
can_change_config
:
return
template_vars
=
{
'kind'
:
'roles'
,
'column_keys'
:
[
'name'
,
'can_see_status'
,
'can_change_config'
,
'can_operate_others'
,
'can_delete_executions'
,
'can_access_api'
,
'can_customize_resources'
,
'can_access_full_zapp_shop'
],
'column_names'
:
[
'Name'
,
'See status page'
,
'Change configuration'
,
'Operate on non-own executions'
,
'Delete executions'
,
'API access'
,
'Web customize resources'
,
'Access all ZApps'
],
'column_types'
:
{
'name'
:
None
,
'can_see_status'
:
'bool'
,
'can_change_config'
:
'bool'
,
'can_operate_others'
:
'bool'
,
'can_delete_executions'
:
'bool'
,
'can_access_api'
:
'bool'
,
'can_customize_resources'
:
'bool'
,
'can_access_full_zapp_shop'
:
'bool'
},
'rows'
:
self
.
api_endpoint
.
role_list
(
self
.
current_user
)
}
self
.
render
(
'admin.jinja2'
,
**
template_vars
)
def
post
(
self
):
"""Form submitted."""
if
self
.
current_user
is
None
or
not
self
.
current_user
.
role
.
can_change_config
:
return
class
QuotasEndpointWeb
(
ZoeWebRequestHandler
):
"""Handler class"""
def
get
(
self
):
"""Quota admin page"""
if
self
.
current_user
is
None
or
not
self
.
current_user
.
role
.
can_change_config
:
return
template_vars
=
{
'kind'
:
'quotas'
,
'column_keys'
:
[
'name'
,
'concurrent_executions'
,
'cores'
,
'memory'
,
'runtime_limit'
],
'column_names'
:
[
'Name'
,
'Concurrent executions'
,
'Cores'
,
'Memory (GiB)'
,
'Run time (hours)'
],
'column_types'
:
{
'name'
:
None
,
'concurrent_executions'
:
'number'
,
'cores'
:
'number'
,
'memory'
:
'bytes'
,
'runtime_limit'
:
'number'
},
'rows'
:
self
.
api_endpoint
.
quota_list
(
self
.
current_user
)
}
self
.
render
(
'admin.jinja2'
,
**
template_vars
)
def
post
(
self
):
"""Form submitted."""
if
self
.
current_user
is
None
or
not
self
.
current_user
.
role
.
can_change_config
:
return
zoe_api/web/status.py
View file @
3e25ff90
...
...
@@ -24,7 +24,7 @@ class StatusEndpointWeb(ZoeWebRequestHandler):
def
get
(
self
):
"""Status and statistics page."""
if
self
.
current_user
is
None
:
if
self
.
current_user
is
None
or
not
self
.
current_user
.
role
.
can_see_status
:
return
stats
=
self
.
api_endpoint
.
statistics_scheduler
()
...
...
zoe_api/web/templates/admin.jinja2
0 → 100644
View file @
3e25ff90
{% extends "base_user.jinja2" %}
{% block title %}Zoe {{ kind }} admin{% endblock %}
{% block custom_head %}
{% endblock %}
{% block content %}
<h2>Zoe {{ kind }} admin</h2>
<table id="exec_list" class="app_list">
<thead>
<tr>
<th>ID</th>
{% for h in column_names %}
<th>{{ h }}</th>
{% endfor %}
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for row in rows|sort(attribute='id') %}
{% set row_index = loop.index0 %}
<tr>
<td><form id="form{{ row_index }}" method="post" action="{{ reverse_url('admin_' + kind) }}"><input type="hidden" name="id" value="{{ row.id }}">{{ row.id }}</form></td>
{% for key in column_keys %}
{% if column_types[key] == None %}
<td>{{ row[key] }}</td>
{% elif column_types[key] == 'text' %}
<td><input form="form{{ row_index }}" type="text" name="{{ key }}" value="{{ row[key] }}" title="{{ key }}"></td>
{% elif column_types[key] == 'number' %}
<td><input form="form{{ row_index }}" type="number" name="{{ key }}" value="{{ row[key] }}" title="{{ key }}"></td>
{% elif column_types[key] == 'bytes' %}
<td><input form="form{{ row_index }}" type="number" name="{{ key }}" value="{{ row[key]/(1024**3) }}" title="{{ key }}"></td>
{% elif column_types[key] == 'bool' %}
{% if row[key] %}
<td><input form="form{{ row_index }}" type="checkbox" name="{{ key }}" checked title="{{ key }}"></td>
{% else %}
<td><input form="form{{ row_index }}" type="checkbox" name="{{ key }}" title="{{ key }}"></td>
{% endif %}
{% elif column_types[key] == 'list' %}
<td>
<select form="form{{ row_index }}" title="{{ key }}" name="{{ key }}">
{% for item in lists[key] %}
{% if row[key] == item %}
<option value="{{ item }}" selected>{{ item }}</option>
{% else %}
<option value="{{ item }}">{{ item }}</option>
{% endif %}
{% endfor %}
</select>
</td>
{% endif %}
{% endfor %}
<td>
<button form="form{{ row_index }}" name="action" type="submit" value="update">Update</button>
<button form="form{{ row_index }}" name="action" type="submit" value="delete">Delete</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
zoe_api/web/templates/base_user.jinja2
View file @
3e25ff90
...
...
@@ -20,6 +20,17 @@
<a href="{{ reverse_url("status") }}">Status</a>
</div>
{% endif %}
{% if user.role.can_change_config %}
<div class="nav-item">
<a href="{{ reverse_url("admin_users") }}">Users</a>
</div>
<div class="nav-item">
<a href="{{ reverse_url("admin_roles") }}">Roles</a>
</div>
<div class="nav-item">
<a href="{{ reverse_url("admin_quotas") }}">Quotas</a>
</div>
{% endif %}
</div>
<div id="user_info">
{{ user.username }} ({{ user.role.name }}) <a href="{{ reverse_url("logout") }}">logout</a>
...
...
zoe_lib/state/quota.py
View file @
3e25ff90
...
...
@@ -65,6 +65,15 @@ class Quota(BaseRecord):
self
.
runtime_limit
=
value
self
.
sql_manager
.
quota_update
(
self
.
id
,
runtime_limit
=
value
)
def
__repr__
(
self
):
return
self
.
name
def
__eq__
(
self
,
other
):
if
isinstance
(
other
,
Quota
):
return
self
.
id
==
other
.
id
else
:
return
False
class
QuotaTable
(
BaseTable
):
"""Abstraction for the quota table in the database."""
...
...
zoe_lib/state/role.py
View file @
3e25ff90
...
...
@@ -51,6 +51,15 @@ class Role(BaseRecord):
'can_access_full_zapp_shop'
:
self
.
can_access_full_zapp_shop
}
def
__repr__
(
self
):
return
self
.
name
def
__eq__
(
self
,
other
):
if
isinstance
(
other
,
Role
):
return
self
.
id
==
other
.
id
else
:
return
False
class
RoleTable
(
BaseTable
):
"""Abstraction for the role table in the database."""
...
...
zoe_lib/state/user.py
View file @
3e25ff90
...
...
@@ -105,6 +105,15 @@ class User(BaseRecord):
"""Get the quota for this user."""
return
self
.
sql_manager
.
quota
.
select
(
only_one
=
True
,
id
=
self
.
quota_id
)
def
__repr__
(
self
):
return
self
.
username
def
__eq__
(
self
,
other
):
if
isinstance
(
other
,
User
):
return
self
.
id
==
other
.
id
else
:
return
False
class
UserTable
(
BaseTable
):
"""Abstraction for the user table in the database."""
...
...
@@ -182,6 +191,6 @@ class UserTable(BaseTable):
def
update
(
self
,
user_id
,
**
fields
):
"""Update a user record."""
if
'password'
in
fields
:
if
'password'
in
fields
and
fields
[
'password'
]
is
not
None
:
fields
[
'password'
]
=
hash_algo
.
hash
(
fields
[
'password'
])
super
().
update
(
user_id
,
**
fields
)
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment