1
0
mirror of https://github.com/dkmstr/openuds.git synced 2024-12-22 13:34:04 +03:00

Advancing on uds client

This commit is contained in:
Adolfo Gómez García 2015-03-23 12:08:39 +01:00
parent d6e031e9e6
commit ccf35fcd49
12 changed files with 287 additions and 108 deletions

View File

@ -34,14 +34,15 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext as _
from uds.core.util import Config
from uds.core.util.State import State
from uds.core.util import log
from uds.core.managers import cryptoManager
from uds.core.util.stats import events
from uds.REST import Handler
from uds.REST import RequestError
from uds.models import UserService
from uds.models import TicketStore
from uds.models import UserService, Transport, ServicePool, User
from uds.core.managers.UserServiceManager import UserServiceManager
from uds.web import errors
from uds.core.managers import cryptoManager
import datetime
import six
@ -51,8 +52,6 @@ import logging
logger = logging.getLogger(__name__)
# Error codes:
# Enclosed methods under /actor path
class Client(Handler):
'''
@ -86,7 +85,65 @@ class Client(Handler):
'''
logger.debug("Client args for GET: {0}".format(self._args))
if len(self._args) < 1:
if len(self._args) != 3:
raise RequestError('Invalid request')
raise RequestError('Invalid request')
try:
data = TicketStore.get(self._args[0])
except Exception:
return Client.result(error=errors.ACCESS_DENIED)
transportId = self._args[2]
password = cryptoManager().xor(self._args[1], data['password']).decode('utf-8')
try:
logger.debug('Kind of service: {}, idService: {}, idTransport: {}, user: {}'.format(data['type'], data['service'], transportId, data['user']))
user = User.objects.get(uuid=data['user'])
if data['type'] == 'A': # This is an assigned service
ads = UserService.objects.get(uuid=data['service'])
else:
ds = ServicePool.objects.get(uuid=data['service'])
# The user is extracted from ticket itself
ds.validateUser(user)
# Now we have to locate an instance of the service, so we can assign it to user.
ads = UserServiceManager.manager().getAssignationForUser(ds, user)
if ads.isInMaintenance() is True:
return Client.result(error=errors.SERVICE_IN_MAINTENANCE)
logger.debug('Found service: {0}'.format(ads))
trans = Transport.objects.get(uuid=transportId)
# Test if the service is ready
if ads.isReady():
log.doLog(ads, log.INFO, "User {0} from {1} has initiated access".format(user.name, self._request.ip), log.WEB)
# If ready, show transport for this service, if also ready ofc
iads = ads.getInstance()
ip = iads.getIp()
events.addEvent(ads.deployed_service, events.ET_ACCESS, username=user.name, srcip=self._request.ip, dstip=ip, uniqueid=ads.unique_id)
if ip is not None:
itrans = trans.getInstance()
if itrans.isAvailableFor(ip):
ads.setConnectionSource(self._request.ip, 'unknown')
log.doLog(ads, log.INFO, "User service ready, rendering transport", log.WEB)
transportInfo = itrans.getUDSTransportData(ads, trans, ip, self.request.os, user, password, self._request)
UserServiceManager.manager().notifyPreconnect(ads, itrans.processedUser(ads, user), itrans.protocol)
return Client.result(transportInfo)
else:
log.doLog(ads, log.WARN, "User service is not accessible (ip {0})".format(ip), log.TRANSPORT)
logger.debug('Transport is not ready for user service {0}'.format(ads))
else:
logger.debug('Ip not available from user service {0}'.format(ads))
else:
log.doLog(ads, log.WARN, "User {0} from {1} tried to access, but machine was not ready".format(user.name, self._request.ip), log.WEB)
# Not ready, show message and return to this page in a while
return Client.result(error=errors.SERVICE_NOT_READY)
except Exception:
logger.exception("Exception")
raise RequestError('Request error')

View File

@ -41,6 +41,8 @@ import array
import uuid
import datetime
import codecs
import random
import string
import logging
import six
@ -141,3 +143,6 @@ class CryptoManager(object):
obj = six.binary_type(obj)
return six.text_type(uuid.uuid5(self._namespace, six.binary_type(obj))).lower() # I believe uuid returns a lowercase uuid always, but in case... :)
def randomString(self, length=40):
return ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(length))

View File

@ -156,7 +156,12 @@ class Transport(Module):
'''
return user.name
def getUDSTransportInfo(self, userService, transport, ip, os, user, password, request):
def getUDSTransportData(self, userService, transport, ip, os, user, password, request):
'''
Must ve overriden
Returns the transport data needed to connect with the userService
This is invoked right before service is accesed (secuentally)
'''
return None
def renderAsHtml(self, userService, transport, ip, request):

View File

@ -77,6 +77,6 @@ def getOsFromRequest(request):
try:
return request.os
except Exception:
request.os = getOsFromUA(request.META['HTTP_USER_AGENT'])
request.os = getOsFromUA(request.META.get('HTTP_USER_AGENT'))
return request.os

View File

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('uds', '0014_permissions'),
]
operations = [
migrations.CreateModel(
name='TicketStore',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('uuid', models.CharField(default=None, max_length=50, unique=True, null=True)),
('stamp', models.DateTimeField()),
('validity', models.IntegerField(default=60)),
('data', models.BinaryField()),
('validator', models.BinaryField(default=None, null=True, blank=True)),
],
options={
'db_table': 'uds_tickets',
},
bases=(models.Model,),
),
]

View File

@ -33,7 +33,7 @@
from __future__ import unicode_literals
__updated__ = '2015-03-02'
__updated__ = '2015-03-23'
from django.db import models
from django.db.models import signals
@ -248,7 +248,7 @@ class DeployedService(UUIDModel):
activePub: Active publication used as "current" publication to make checks
'''
now = getSqlDatetime()
if activePub == None:
if activePub is None:
logger.error('No active publication, don\'t know what to erase!!! (ds = {0})'.format(self))
return
for ap in self.publications.exclude(id=activePub.id):

View File

@ -37,13 +37,11 @@ from django.db import models
from uds.models.UUIDModel import UUIDModel
from uds.models.Util import getSqlDatetime
import datetime
import pickle
import base64
import string
import random
import logging
logger = logging.getLogger(__name__)
@ -53,13 +51,14 @@ class TicketStore(UUIDModel):
'''
Image storing on DB model
This is intended for small images (i will limit them to 128x128), so storing at db is fine
'''
DEFAULT_VALIDITY = 60
stamp = models.DateTimeField() # Date creation or validation of this entry
validity = models.IntegerField(default=60) # Duration allowed for this ticket to be valid, in seconds
data = models.BinaryField() # Associated ticket data
validator = models.BinaryField(null=True, blank=True, default=None) # Associated validator for this ticket
class Meta:
'''
@ -68,17 +67,58 @@ class TicketStore(UUIDModel):
db_table = 'uds_tickets'
app_label = 'uds'
@staticmethod
def create(data, validity=60):
return TicketStore.objects.create(stamp=getSqlDatetime(), data=pickle.dumps(data), validity=validity).uuid
def genUuid(self):
return TicketStore.generateUuid()
@staticmethod
def get(uuid):
def generateUuid():
# more secure is this:
# ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(40))
return ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(40))
@staticmethod
def create(data, validator=None, validity=DEFAULT_VALIDITY):
if validator is not None:
validator = pickle.dumps(validator)
return TicketStore.objects.create(stamp=getSqlDatetime(), data=pickle.dumps(data), validator=validator, validity=validity).uuid
@staticmethod
def store(uuid, data, validator=None, validity=DEFAULT_VALIDITY):
if validator is not None:
validator = pickle.dumps(validator)
try:
t = TicketStore.objects.get(uuid=uuid)
if datetime.timedelta(seconds=t.validity) + t.stamp < getSqlDatetime():
t.data = pickle.dumps(data)
t.stamp = getSqlDatetime()
t.validity = validity
t.save()
except TicketStore.DoesNotExist:
t = TicketStore.objects.create(uuid=uuid, stamp=getSqlDatetime(), data=pickle.dumps(data), validator=validator, validity=validity)
@staticmethod
def get(uuid, invalidate=True):
try:
t = TicketStore.objects.get(uuid=uuid)
validity = datetime.timedelta(seconds=t.validity)
now = getSqlDatetime()
if t.stamp + validity < now:
raise Exception('Not valid anymore')
return pickle.loads(t.data)
data = pickle.loads(t.data)
# If has validator, execute it
if t.validator is not None:
validator = pickle.loads(t.validator)
if validator(data) is False:
raise Exception('Validation failed')
if invalidate is True:
t.stamp = now - validity - datetime.timedelta(seconds=1)
t.save()
return data
except TicketStore.DoesNotExist:
raise Exception('Does not exists')
@ -92,3 +132,11 @@ class TicketStore(UUIDModel):
t.save()
except TicketStore.DoesNotExist:
raise Exception('Does not exists')
def __unicode__(self):
if self.validator is not None:
validator = pickle.loads(self.validator)
else:
validator = None
return 'Ticket id: {}, Stamp: {}, Validity: {}, Validator: {}, Data: {}'.format(self.uuid, self.stamp, self.validity, validator, pickle.loads(self.data))

View File

@ -33,7 +33,7 @@
from __future__ import unicode_literals
__updated__ = '2014-11-12'
__updated__ = '2015-03-23'
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
@ -57,10 +57,13 @@ class UUIDModel(models.Model):
class Meta:
abstract = True
def genUuid(self):
return generateUuid()
# Override default save to add uuid
def save(self, *args, **kwargs):
if self.uuid is None or self.uuid == '':
self.uuid = generateUuid()
self.uuid = self.genUuid()
elif self.uuid != self.uuid.lower():
self.uuid = self.uuid.lower() # If we modify uuid elsewhere, ensure that it's stored in lower case

View File

@ -157,24 +157,30 @@ launchSafari = (el, url, alt) ->
), 800
@uds.onLink = ->
uds.launch = (el) ->
url = el.attr('data-href')
url = if url? then url else el.attr('href')
alt = el.attr('data-href-alt')
if uds.firefox
launchMozilla this, url, alt
else if uds.chrome
launchChrome this, url, alt
else if uds.safari
launchSafari this, url, alt
else if uds.ie
launchIE this, url, alt
return
uds.onLink = ->
$('.uds-service-link').on('click', ((e) ->
e.preventDefault()
el = $(this)
url = el.attr('href')
alt = el.attr('data-href-alt')
if uds.firefox
launchMozilla this, url, alt
else if uds.chrome
launchChrome this, url, alt
else if uds.safari
launchSafari this, url, alt
else if uds.ie
launchIE this, url, alt
uds.launch $(this)
return
))
return
return

View File

@ -8,14 +8,14 @@
{% for ser in services %}
{% if ser.transports %}
<div class="service-container">
{% with trans=ser.transports|first numTransports=ser.transports|length %}
<div class="service{% if not java and trans.needsJava %} nojava{% endif %}{% if ser.maintenance %} maintenance{% endif %}{% if ser.in_use %} inuse{% endif %}"
{% with trans=ser.transports|first numTransports=ser.transports|length link=host|add:ser.ticket|add:'/'|add:scrambler|add:'/' %}
<div class="service{% if ser.maintenance %} maintenance{% endif %}{% if ser.in_use %} inuse{% endif %}"
{% if ser.maintenance %}
data-content="{% trans "Under maintenance" %}"
{% elif ser.in_use %}
data-content="{%trans "Currently in use" %}"
{% endif %}
data-url="{% url "uds.web.views.service" idService=ser.id idTransport=trans.id %}">
data-href="{{ link }}{{ trans.id }}">
<div class="service-image">
<img src="{% url "uds.web.views.serviceImage" idImage=ser.imageId %}" />
</div>
@ -27,7 +27,7 @@
{% if ser.show_transports and numTransports > 1 %}
<div class="modal fade in">
<div class="modal-dialog">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
@ -36,7 +36,7 @@
<div class="modal-body">
<ul>
{% for trans in ser.transports %}
<p><a href="{% url "uds.web.views.service" idService=ser.id idTransport=trans.id %}"><img src="{% url "uds.web.views.transportIcon" idTrans=trans.id %}" alt="{{ trans.name }}" />{{ trans.name }}</a></p>
<li><a class="uds-service-transport" data-href-alt="{% url 'uds.web.views.client_downloads' %}" href="{{ link }}{{ trans.id }}"><img src="{% url "uds.web.views.transportIcon" idTrans=trans.id %}" alt="{{ trans.name }}" />{{ trans.name }}</a></li>
{% endfor %}
</ul>
@ -72,30 +72,9 @@
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
{% if not java %}
<div class="modal fade" id="no-java-dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">{% trans "Java not found" %}</h4>
</div>
<div class="modal-body text-center">
<p>{% trans "Java is not available on your browser, and the selected transport needs it." %}</p>
<p>{% trans "Please, install latest version from" %} <a href="http://www.java.com" target="_blank">{% trans "Java website" %}</a> {% trans "and restart browser" %}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
{% endif %}
{% if user.isStaff %}
{% if user.isStaff %}
<div class="panel panel-warning">
<div class="panel-heading">
{% trans "Administrator info panel" %}
@ -108,7 +87,7 @@
<p>{% trans "OS" %}: {{ request.session.OS.OS }}</p>
</div>
</div>
{% endif %}
{% endif %}
{% endblock %}
@ -212,18 +191,28 @@
$('.inuse').popover({container: 'body', trigger: 'hover', delay: { show: 500, hide: 100 }, placement: 'auto top'});
$('div.service:not(.maintenance):not(.nojava)').on("click", function (event){
if($(this).hasClass('disabled'))
return;
$('div.service').addClass('disabled');
window.location = $(this).attr('data-url');
$('div.service:not(.maintenance)').on("click", function (event){
event.preventDefault();
uds.launch($(this));
return false;
}).on("mouseenter mouseleave", function (event) {
$(this).toggleClass('over');
});
$('div.service:not(.maintenance):not(.nojava) > span.gear > span.fa').on("click", function (event) {
$('.uds-service-transport').on("click", function (event){
event.preventDefault();
var modal = $(this).parent().parent().parent().parent().parent().parent();
modal.modal('hide');
uds.launch($(this));
return false;
})
$('div.service:not(.maintenance) > span.gear > span.fa').on("click", function (event) {
event.stopPropagation();
event.preventDefault();
$(this).parent().parent().next().modal();
return false;
});
@ -235,15 +224,6 @@
return false;
});
{% if not java %}
$(".nojava").click( function(event) {
$('#no-java-dialog').modal({
keyboard: false
})
return false;
});
{% endif %}
});
</script>
{% endblock %}

View File

@ -63,6 +63,7 @@ INVALID_CALLBACK = 9
INVALID_REQUEST = 10
BROWSER_NOT_SUPPORTED = 11,
SERVICE_IN_MAINTENANCE = 12
SERVICE_NOT_READY = 13
strings = [
@ -78,7 +79,8 @@ strings = [
_('Invalid authenticator'),
_('Invalid request received'),
_('Your browser is not supported. Please, upgrade it to a modern HTML5 browser like Firefox or Chrome'),
_('The requested service is in maintenance mode')
_('The requested service is in maintenance mode'),
_('The service is not ready')
]

View File

@ -30,21 +30,22 @@
'''
from __future__ import unicode_literals
__updated__ = '2015-02-28'
__updated__ = '2015-03-23'
from django.shortcuts import render_to_response
from django.shortcuts import render
from django.shortcuts import redirect
from django.template import RequestContext
from uds.core.auths.auth import webLoginRequired
from uds.core.auths.auth import webLoginRequired, webPassword
from uds.models import DeployedService, Transport, UserService, Network
from uds.models import DeployedService, Transport, UserService, Network, TicketStore
from uds.core.util.Config import GlobalConfig
from uds.core.util import OsDetector
from uds.core.ui import theme
from uds.core.managers.UserServiceManager import UserServiceManager
from uds.core.managers import cryptoManager
import logging
@ -67,8 +68,15 @@ def index(request):
:param request: http request
'''
# Session data
os = OsDetector.getOsFromRequest(request)
java = request.session.get('java', None)
os = request.os
scrambler = cryptoManager().randomString(32)
password = cryptoManager().xor(webPassword(request), scrambler)
if request.is_secure():
proto = 'udss'
else:
proto = 'uds'
# We look for services for this authenticator groups. User is logged in in just 1 authenticator, so his groups must coincide with those assigned to ds
groups = list(request.user.getGroups())
@ -103,13 +111,28 @@ def index(request):
imageId = svr.deployed_service.image.uuid
else:
imageId = 'x' # Invalid
services.append({'id': 'A' + svr.uuid,
'name': svr['name'],
'transports': trans,
'imageId': imageId,
'show_transports': svr.deployed_service.show_transports,
'maintenance': svr.deployed_service.service.provider.maintenance_mode,
'in_use': svr.in_use})
# Generate ticket
data = {
'type': 'A',
'service': svr.uuid,
'user': request.user.uuid,
'password': password
}
ticket = TicketStore.create(data)
link = '{}://{}/{}'.format(proto, ticket, scrambler)
services.append({
'id': 'A' + svr.uuid,
'name': svr['name'],
'transports': trans,
'imageId': imageId,
'show_transports': svr.deployed_service.show_transports,
'maintenance': svr.deployed_service.service.provider.maintenance_mode,
'in_use': svr.in_use,
'ticket': ticket,
})
logger.debug(services)
@ -133,13 +156,26 @@ def index(request):
else:
in_use = ads.in_use
services.append({'id': 'F' + svr.uuid,
'name': svr.name,
'transports': trans,
'imageId': imageId,
'show_transports': svr.show_transports,
'maintenance': svr.service.provider.maintenance_mode,
'in_use': in_use})
# Generate tickets for every transport
data = {
'type': 'F',
'service': svr.uuid,
'user': request.user.uuid,
'password': password
}
ticket = TicketStore.create(data)
services.append({
'id': 'F' + svr.uuid,
'name': svr.name,
'transports': trans,
'imageId': imageId,
'show_transports': svr.show_transports,
'maintenance': svr.service.provider.maintenance_mode,
'in_use': in_use,
'ticket': ticket,
})
logger.debug('Services: {0}'.format(services))
@ -150,8 +186,16 @@ def index(request):
request.session['autorunDone'] = '1'
return redirect('uds.web.views.service', idService=services[0]['id'], idTransport=services[0]['transports'][0]['id'])
response = render_to_response(theme.template('index.html'),
{'services': services, 'java': java, 'ip': request.ip, 'nets': nets, 'transports': validTrans},
context_instance=RequestContext(request)
)
response = render_to_response(
theme.template('index.html'),
{
'services': services,
'ip': request.ip,
'nets': nets,
'transports': validTrans,
'host': proto + '://' + request.build_absolute_uri('/').split('//')[1],
'scrambler': scrambler,
},
context_instance=RequestContext(request)
)
return response