Initial UDS Proxy for networks behind firewalls

This commit is contained in:
Adolfo Gómez García 2017-01-18 20:25:13 +01:00
parent 5897cf33f2
commit ab8cb7a7e0
6 changed files with 106 additions and 408 deletions

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2014 Virtual Cable S.L. # Copyright (c) 2017 Virtual Cable S.L.
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without modification, # Redistribution and use in source and binary forms, with or without modification,
@ -33,11 +33,12 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _, ugettext from django.utils.translation import ugettext_lazy as _, ugettext
from uds.models import Account, AccountUsage from uds.models import Account
from uds.core.util import permissions from uds.core.util import permissions
from uds.REST.model import ModelHandler from uds.REST.model import ModelHandler
from .accountsusage import AccountsUsage
import logging import logging
@ -51,7 +52,7 @@ class Accounts(ModelHandler):
Processes REST requests about calendars Processes REST requests about calendars
''' '''
model = Account model = Account
detail = {'usage': AccountUsage} detail = {'usage': AccountsUsage }
save_fields = ['name', 'comments', 'tags'] save_fields = ['name', 'comments', 'tags']

View File

@ -3,125 +3,59 @@ gui.accounts = new GuiElement(api.accounts, "accounts")
gui.accounts.link = (event) -> gui.accounts.link = (event) ->
"use strict" "use strict"
# Button definition to trigger "Test" action useTable = undefined
testButton = testButton: clearUsage = ->
text: gettext("Test") if useTable
css: "btn-info" $tbl = $(useTable).dataTable()
# Clears the log of the detail, in this case, the log of "users"
# Memory saver :-)
detailLogTable = null
clearDetailLog = ->
if detailLogTable?
$tbl = $(detailLogTable).dataTable()
$tbl.fnClearTable() $tbl.fnClearTable()
$tbl.fnDestroy() $tbl.fnDestroy()
detailLogTable = null useTable = undefined
$("#users-log-placeholder").empty() $("#usage-placeholder").empty()
return
# Clears the details
# Memory saver :-)
prevTables = []
clearDetails = ->
clearDetailLog()
$.each prevTables, (undefined_, tbl) ->
$tbl = $(tbl).dataTable()
$tbl.fnClearTable()
$tbl.fnDestroy()
return
$("#users-placeholder").empty()
$("#groups-placeholder").empty()
$("#logs-placeholder").empty()
$("#detail-placeholder").addClass "hidden" $("#detail-placeholder").addClass "hidden"
prevTables = []
return
# Search button event generator for user/group
searchForm = (parentModalId, type, id, title, searchLabel, resultsLabel) ->
errorModal = gui.failRequestModalFnc(gettext("Search error"))
srcSelector = parentModalId + " input[name=\"name\"]"
$(parentModalId + " .button-search").on "click", ->
api.templates.get "search", (tmpl) -> # Get form template
modalId = gui.launchModal(title, api.templates.evaluate(tmpl,
search_label: searchLabel
results_label: resultsLabel
),
actionButton: "<button type=\"button\" class=\"btn btn-success button-accept\">" + gettext("Accept") + "</button>"
)
$searchInput = $(modalId + " input[name=\"search\"]")
$select = $(modalId + " select[name=\"results\"]")
$searchButton = $(modalId + " .button-do-search")
$saveButton = $(modalId + " .button-accept")
$searchInput.val $(srcSelector).val()
$saveButton.on "click", ->
value = $select.val()
if value
$(srcSelector).val value
$(modalId).modal "hide"
return
$searchButton.on "click", ->
$searchButton.addClass "disabled"
term = $searchInput.val()
api.accounts.search id, type, term, ((data) ->
$searchButton.removeClass "disabled"
$select.empty()
gui.doLog data
$.each data, (undefined_, value) ->
$select.append "<option value=\"" + value.id + "\">" + value.id + " (" + value.name + ")</option>"
return
return
), (jqXHR, textStatus, errorThrown) ->
$searchButton.removeClass "disabled"
errorModal jqXHR, textStatus, errorThrown
return
return
$(modalId + " form").submit (event) ->
event.preventDefault()
$searchButton.click()
return
$searchButton.click() if $searchInput.val() isnt ""
return
return
return return
api.templates.get "accounts", (tmpl) -> api.templates.get "accounts", (tmpl) ->
gui.clearWorkspace() gui.clearWorkspace()
gui.appendToWorkspace api.templates.evaluate(tmpl, gui.appendToWorkspace api.templates.evaluate(tmpl,
auths: "auths-placeholder" accounts: "accounts-placeholder"
auths_info: "auths-info-placeholder" usage: "usage-placeholder"
users: "users-placeholder"
users_log: "users-log-placeholder"
groups: "groups-placeholder"
logs: "logs-placeholder"
) )
gui.setLinksEvents() gui.setLinksEvents()
# Append tabs click events gui.accounts.table
$(".bottom_tabs").on "click", (event) -> icon: 'accounts'
gui.doLog event.target container: "accounts-placeholder"
setTimeout (-> rowSelect: "single"
$($(event.target).attr("href") + " span.fa-refresh").click()
return onRefresh: (tbl) ->
), 10 clearUsage()
return return
tableId = gui.accounts.table( onRowSelect: (selected) ->
icon: 'accounts' clearUsage()
container: "auths-placeholder" $("#detail-placeholder").removeClass "hidden"
rowSelect: "multi" # gui.tools.blockUI()
id = selected[0].id
usage = new GuiElement(api.accounts.detail(id, "usage", { permission: selected[0].permission }), "rules")
usageTable = usage.table(
icon: 'calendars'
container: "usage-placeholder"
rowSelect: "single"
buttons: [
"new"
"edit"
"delete"
"xls"
]
onLoad: (k) ->
# gui.tools.unblockUI()
return # null return
)
return
onRowDeselect: ->
clearUsage()
return
buttons: [ buttons: [
"new" "new"
"edit" "edit"
@ -129,311 +63,8 @@ gui.accounts.link = (event) ->
"xls" "xls"
"permissions" "permissions"
] ]
onNew: gui.methods.typedNew(gui.calendars, gettext("New calendar"), gettext("Calendar creation error"))
onFoundUuid: (item) -> onEdit: gui.methods.typedEdit(gui.calendars, gettext("Edit calendar"), gettext("Calendar saving error"))
# Invoked if our table has found a "desirable" item (uuid) onDelete: gui.methods.del(gui.calendars, gettext("Delete calendar"), gettext("Calendar deletion error"))
if gui.lookup2Uuid?
type = gui.lookup2Uuid[0]
gui.lookupUuid = gui.lookup2Uuid.substr(1)
gui.lookup2Uuid = null
setTimeout( () ->
if type == 'g'
$('a[href="#groups-placeholder"]').tab('show')
$("#groups-placeholder span.fa-refresh").click()
else
$('a[href="#users-placeholder_tab"]').tab('show')
$("#users-placeholder_tab span.fa-refresh").click()
, 500)
onRefresh: (tbl) ->
gui.doLog 'Refresh called for accounts'
clearDetails()
return
onRowDeselect: (deselected, dtable) ->
clearDetails()
return
onRowSelect: (selected) ->
clearDetails()
if selected.length > 1
return
# We can have lots of users, so memory can grow up rapidly if we do not keep thins clean
# To do so, we empty previous table contents before storing new table contents
# Anyway, TabletTools will keep "leaking" memory, but we can handle a little "leak" that will be fixed as soon as we change the section
$("#detail-placeholder").removeClass "hidden"
$('#detail-placeholder a[href="#auths-info-placeholder"]').tab('show')
gui.tools.blockUI()
# Load provider "info"
gui.methods.typedShow gui.accounts, selected[0], '#auths-info-placeholder .well', gettext('Error accessing data')
id = selected[0].id
type = gui.accounts.types[selected[0].type]
gui.doLog "Type", type
user = new GuiElement(api.accounts.detail(id, "users", { permission: selected[0].permission }), "users")
group = new GuiElement(api.accounts.detail(id, "groups", { permission: selected[0].permission }), "groups")
grpTable = group.table(
icon: 'groups'
container: "groups-placeholder"
doNotLoadData: true
rowSelect: "multi"
buttons: [
"new"
"edit"
"delete"
"xls"
]
onLoad: (k) ->
gui.tools.unblockUI()
return
onEdit: (value, event, table, refreshFnc) ->
exec = (groups_all) ->
gui.tools.blockUI()
api.templates.get "group", (tmpl) -> # Get form template
group.rest.item value.id, (item) -> # Get item to edit
# Creates modal
modalId = gui.launchModal(gettext("Edit group") + " <b>" + item.name + "</b>", api.templates.evaluate(tmpl,
id: item.id
type: item.type
meta_if_any: item.meta_if_any
groupname: item.name
groupname_label: type.groupNameLabel
comments: item.comments
state: item.state
external: type.isExternal
canSearchGroups: type.canSearchGroups
groups: item.groups
groups_all: groups_all
))
gui.tools.applyCustoms modalId
gui.tools.unblockUI()
$(modalId + " .button-accept").click ->
fields = gui.forms.read(modalId)
gui.doLog "Fields", fields
group.rest.save fields, ((data) -> # Success on put
$(modalId).modal "hide"
refreshFnc()
gui.notify gettext("Group saved"), "success"
return
), gui.failRequestModalFnc("Error saving group", true)
return
return
return
return
if value.type is "meta"
# Meta will get all groups
group.rest.overview (groups) ->
exec groups
return
else
exec()
return
onNew: (t, table, refreshFnc) ->
exec = (groups_all) ->
gui.tools.blockUI()
api.templates.get "group", (tmpl) -> # Get form template
# Creates modal
if t is "meta"
title = gettext("New meta group")
else
title = gettext("New group")
modalId = gui.launchModal(title, api.templates.evaluate(tmpl,
type: t
groupname_label: type.groupNameLabel
external: type.isExternal
canSearchGroups: type.canSearchGroups
groups: []
groups_all: groups_all
))
gui.tools.unblockUI()
gui.tools.applyCustoms modalId
searchForm modalId, "group", id, gettext("Search groups"), gettext("Group"), gettext("Groups found") # Enable search button click, if it exist ofc
$(modalId + " .button-accept").click ->
fields = gui.forms.read(modalId)
gui.doLog "Fields", fields
group.rest.create fields, ((data) -> # Success on put
$(modalId).modal "hide"
refreshFnc()
gui.notify gettext("Group saved"), "success"
return
), gui.failRequestModalFnc(gettext("Group saving error"), true)
return
return
return
if t is "meta"
# Meta will get all groups
group.rest.overview (groups) ->
exec groups
return
else
exec()
return
onDelete: gui.methods.del(group, gettext("Delete group"), gettext("Group deletion error"))
)
tmpLogTable = null
# New button will only be shown on accounts that can create new users
usrButtons = [
"edit"
"delete"
"xls"
]
usrButtons = ["new"].concat(usrButtons) if type.canCreateUsers # New is first button
usrTable = user.table(
icon: 'users'
container: "users-placeholder"
doNotLoadData: true
rowSelect: "multi"
onRowSelect: (uselected) ->
gui.doLog 'User row selected ', uselected
gui.tools.blockUI()
uId = uselected[0].id
clearDetailLog()
tmpLogTable = user.logTable(uId,
container: "users-log-placeholder"
onLoad: ->
detailLogTable = tmpLogTable
gui.tools.unblockUI()
return
)
return
onRowDeselect: ->
clearDetailLog()
return
buttons: usrButtons
deferedRender: true # Use defered rendering for users, this table can be "huge"
scrollToTable: false
onLoad: (k) ->
gui.tools.unblockUI()
return
onRefresh: ->
gui.doLog "Refreshing"
clearDetailLog()
return
onEdit: (value, event, table, refreshFnc) ->
password = "#æð~¬ŋ@æß”¢€~½¬@#~þ¬@|" # Garbage for password (to detect change)
gui.tools.blockUI()
api.templates.get "user", (tmpl) -> # Get form template
group.rest.overview (groups) -> # Get groups
user.rest.item value.id, (item) -> # Get item to edit
# Creates modal
modalId = gui.launchModal(gettext("Edit user") + " <b>" + value.name + "</b>", api.templates.evaluate(tmpl,
id: item.id
username: item.name
username_label: type.userNameLabel
realname: item.real_name
comments: item.comments
state: item.state
staff_member: item.staff_member
is_admin: item.is_admin
needs_password: type.needsPassword
password: (if type.needsPassword then password else undefined)
password_label: type.passwordLabel
groups_all: groups
groups: item.groups
external: type.isExternal
canSearchUsers: type.canSearchUsers
))
gui.tools.applyCustoms modalId
gui.tools.unblockUI()
$(modalId + " .button-accept").click ->
fields = gui.forms.read(modalId)
# If needs password, and password has changed
gui.doLog "passwords", type.needsPassword, password, fields.password
delete fields.password if fields.password is password if type.needsPassword
gui.doLog "Fields", fields
user.rest.save fields, ((data) -> # Success on put
$(modalId).modal "hide"
refreshFnc()
gui.notify gettext("User saved"), "success"
return
), gui.failRequestModalFnc(gettext("User saving error"), true)
return
return
return
return
return
onNew: (undefined_, table, refreshFnc) ->
gui.tools.blockUI()
api.templates.get "user", (tmpl) -> # Get form template
group.rest.overview (groups) -> # Get groups
# Creates modal
modalId = gui.launchModal(gettext("New user"), api.templates.evaluate(tmpl,
username_label: type.userNameLabel
needs_password: type.needsPassword
password_label: type.passwordLabel
groups_all: groups
groups: []
external: type.isExternal
canSearchUsers: type.canSearchUsers
))
gui.tools.applyCustoms modalId
gui.tools.unblockUI()
searchForm modalId, "user", id, gettext("Search users"), gettext("User"), gettext("Users found") # Enable search button click, if it exist ofc
$(modalId + " .button-accept").click ->
fields = gui.forms.read(modalId)
# If needs password, and password has changed
gui.doLog "Fields", fields
user.rest.create fields, ((data) -> # Success on put
$(modalId).modal "hide"
refreshFnc()
gui.notify gettext("User saved"), "success"
return
), gui.failRequestModalFnc(gettext("User saving error"), true)
return
return
return
return
onDelete: gui.methods.del(user, gettext("Delete user"), gettext("User deletion error"))
)
logTable = gui.accounts.logTable(id,
container: "logs-placeholder"
doNotLoadData: true
)
# So we can destroy the tables beforing adding new ones
prevTables.push grpTable
prevTables.push usrTable
prevTables.push logTable
false
onNew: gui.methods.typedNew(gui.accounts, gettext("New authenticator"), gettext("Authenticator creation error"), testButton)
onEdit: gui.methods.typedEdit(gui.accounts, gettext("Edit authenticator"), gettext("Authenticator saving error"), testButton)
onDelete: gui.methods.del(gui.accounts, gettext("Delete authenticator"), gettext("Authenticator deletion error"))
)
return
false false
false

View File

@ -204,6 +204,7 @@
{% js_template 'permissions_add' %} {% js_template 'permissions_add' %}
{% js_template 'reports' %} {% js_template 'reports' %}
{% js_template 'calendars' %} {% js_template 'calendars' %}
{% js_template 'accounts' %}
<!-- utility pages --> <!-- utility pages -->
{% js_template 'request_failed' %} {% js_template 'request_failed' %}

17
udsProxy/.project Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>udsProxy</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.python.pydev.pythonNature</nature>
</natures>
</projectDescription>

8
udsProxy/.pydevproject Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/${PROJECT_DIR_NAME}/src</path>
</pydev_pathproperty>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
</pydev_project>

40
udsProxy/src/udsProxy.py Normal file
View File

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import SimpleHTTPServer
import SocketServer
PORT = 9090
if __name__ == "__main__":
Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
httpd = SocketServer.TCPServer(("", PORT), Handler)
print "Serving at port ", PORT
httpd.serve_forever()