Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
B
B.I.T.S. Server
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
Operations
Operations
Incidents
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Projects
B.I.T.S. 4.0
B.I.T.S. Server
Commits
c5fefeb0
Commit
c5fefeb0
authored
Nov 16, 2014
by
Daniele Iamartino
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #22 from BitsDevelopmentTeam/macroster
Presence list (roster) Good job!
parents
c418ef91
d74e2f27
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
140 additions
and
16 deletions
+140
-16
bitsd.conf
bitsd.conf
+6
-0
bitsd/common.py
bitsd/common.py
+9
-0
bitsd/persistence/models.py
bitsd/persistence/models.py
+19
-2
bitsd/properties.py
bitsd/properties.py
+12
-0
bitsd/server/__init__.py
bitsd/server/__init__.py
+2
-1
bitsd/server/assets/default.css
bitsd/server/assets/default.css
+9
-0
bitsd/server/handlers.py
bitsd/server/handlers.py
+70
-12
bitsd/server/templates/admin.html
bitsd/server/templates/admin.html
+12
-0
bitsd/server/uimodules.py
bitsd/server/uimodules.py
+1
-1
No files found.
bitsd.conf
View file @
c5fefeb0
...
...
@@ -83,3 +83,9 @@ This is also a valid Python file, but don't abuse that.
## ReCaptcha private key
#recaptcha_privkey = "privkey"
## Authentication token for live presence updates
#mac_update_password = "default"
## Minimum number of seconds between two successive MAC updates.
mac_update_interval
=
0
bitsd/common.py
View file @
c5fefeb0
...
...
@@ -7,6 +7,7 @@
#
"""Common elements needed by all modules."""
from
itertools
import
izip_longest
from
tornado.netutil
import
bind_sockets
,
bind_unix_socket
from
tornado.options
import
options
...
...
@@ -19,6 +20,14 @@ import os
LOG
=
logging
.
getLogger
(
'tornado.general'
)
def
secure_compare
(
a
,
b
):
"""Reasonably safe way to securely compare two strings.
Works in constant time to prevent timing attacks.
"""
return
sum
(
1
for
x
,
y
in
izip_longest
(
a
,
b
)
if
x
!=
y
)
==
0
def
bind
(
server
,
port
,
usocket
,
address
=
None
):
"""Make server listen on port (inet socket).
If given, prefer `usocket`, path to a unix socket.
...
...
bitsd/persistence/models.py
View file @
c5fefeb0
...
...
@@ -15,7 +15,7 @@ import re
from
datetime
import
datetime
from
sqlalchemy
import
Column
,
ForeignKey
,
UniqueConstraint
,
Index
from
sqlalchemy.orm
import
relationship
from
sqlalchemy.orm
import
relationship
,
backref
from
sqlalchemy.types
import
Integer
,
Float
,
DateTime
,
Enum
,
Text
,
BigInteger
,
String
,
UnicodeText
from
sqlalchemy.ext.declarative
import
declarative_base
...
...
@@ -110,7 +110,7 @@ class Message(Base):
timestamp
=
Column
(
DateTime
,
primary_key
=
True
,
default
=
datetime
.
now
)
message
=
Column
(
Text
,
nullable
=
False
)
author
=
relationship
(
"User"
)
author
=
relationship
(
"User"
,
backref
=
backref
(
"messages"
,
order_by
=
timestamp
)
)
def
__init__
(
self
,
userid
,
message
):
self
.
userid
=
userid
...
...
@@ -200,3 +200,20 @@ class LoginAttempt(Base):
def
__str__
(
self
):
return
'Failed attempt for `{self.username}` from {self.ipaddress} at {self.timestamp}'
.
format
(
self
=
self
)
class
MACToUser
(
Base
):
__tablename__
=
'MACToUser'
__table_args__
=
{
'mysql_engine'
:
'InnoDB'
,
'mysql_charset'
:
'utf8mb4'
}
userid
=
Column
(
Integer
,
ForeignKey
(
"User.userid"
))
mac_hash
=
Column
(
String
(
length
=
64
),
primary_key
=
True
)
user
=
relationship
(
"User"
,
backref
=
backref
(
"macs"
,
order_by
=
userid
))
def
__init__
(
self
,
userid
,
mac_hash
):
self
.
userid
=
userid
self
.
mac_hash
=
mac_hash
def
__str__
(
self
):
return
'User with ID {self.userid} has MAC with hash {self.mac_hash}'
.
format
(
self
=
self
)
bitsd/properties.py
View file @
c5fefeb0
...
...
@@ -147,3 +147,15 @@ define("usocket_mode",
help
=
"Permissions for chmod on the unix sockets"
,
group
=
"Networking"
)
define
(
"mac_update_password"
,
default
=
"default"
,
help
=
"Authentication token for live presence updates"
,
group
=
"Networking"
)
define
(
"mac_update_interval"
,
default
=
0
,
help
=
"Minimum number of seconds between two successive MAC updates."
,
group
=
"Networking"
)
bitsd/server/__init__.py
View file @
c5fefeb0
...
...
@@ -38,7 +38,8 @@ def start():
(
r'/logout'
,
handlers
.
LogoutPageHandler
),
(
r'/admin'
,
handlers
.
AdminPageHandler
),
(
r'/message'
,
handlers
.
MessagePageHandler
),
(
r'/data.php'
,
handlers
.
RTCHandler
)
(
r'/data.php'
,
handlers
.
RTCHandler
),
(
r'/macupdate'
,
handlers
.
MACUpdateHandler
),
],
ui_modules
=
uimodules
,
gzip
=
True
,
...
...
bitsd/server/assets/default.css
View file @
c5fefeb0
...
...
@@ -215,3 +215,12 @@ nav#paginator ul li {
width
:
300px
;
margin
:
0
auto
;
}
#roster
{
width
:
200px
;
margin
:
0
auto
;
}
#roster
li
{
text-align
:
left
;
}
bitsd/server/handlers.py
View file @
c5fefeb0
...
...
@@ -10,20 +10,22 @@
"""
HTTP requests handlers.
"""
import
json
import
markdown
import
datetime
from
datetime
import
datetime
,
timedelta
from
sqlalchemy
import
distinct
from
sqlalchemy.exc
import
IntegrityError
import
tornado.web
from
tornado.web
import
MissingArgumentError
,
HTTPError
,
RequestHandler
import
tornado.websocket
import
tornado.auth
from
tornado.options
import
options
import
bitsd.listener.notifier
as
notifier
from
bitsd.persistence.engine
import
session_scope
from
bitsd.persistence.models
import
Status
from
bitsd.persistence.engine
import
session_scope
,
persist
from
bitsd.persistence.models
import
Status
,
User
,
MACToUser
,
LoginAttempt
from
.auth
import
verify
,
DoSError
from
.presence
import
PresenceForecaster
...
...
@@ -31,7 +33,7 @@ from .notifier import MessageNotifier
import
bitsd.persistence.query
as
query
from
bitsd.common
import
LOG
from
bitsd.common
import
LOG
,
secure_compare
def
cache
(
seconds
):
...
...
@@ -51,8 +53,8 @@ def cache(seconds):
"""
def
set_cacheable
(
get_function
):
def
wrapper
(
self
,
*
args
,
**
kwargs
):
self
.
set_header
(
"Expires"
,
datetime
.
datetime
.
utcnow
()
+
datetime
.
timedelta
(
seconds
=
seconds
))
self
.
set_header
(
"Expires"
,
datetime
.
utcnow
()
+
timedelta
(
seconds
=
seconds
))
self
.
set_header
(
"Cache-Control"
,
"max-age="
+
str
(
seconds
))
return
get_function
(
self
,
*
args
,
**
kwargs
)
return
wrapper
...
...
@@ -66,7 +68,7 @@ def broadcast(message):
StatusHandler
.
CLIENTS
.
broadcast
(
message
)
class
BaseHandler
(
tornado
.
web
.
RequestHandler
):
class
BaseHandler
(
RequestHandler
):
"""Base requests handler"""
USER_COOKIE_NAME
=
"usertoken"
...
...
@@ -275,8 +277,11 @@ class AdminPageHandler(BaseHandler):
@
tornado
.
web
.
authenticated
def
get
(
self
):
"""Display the admin page."""
self
.
render
(
'templates/admin.html'
,
page_message
=
'Very secret information here'
)
self
.
render
(
'templates/admin.html'
,
page_message
=
'Very secret information here'
,
roster
=
MACUpdateHandler
.
ROSTER
)
@
tornado
.
web
.
authenticated
def
post
(
self
):
...
...
@@ -307,7 +312,11 @@ class AdminPageHandler(BaseHandler):
message
=
"Errore: modifica troppo veloce!"
raise
finally
:
self
.
render
(
'templates/admin.html'
,
page_message
=
message
)
self
.
render
(
'templates/admin.html'
,
page_message
=
message
,
roster
=
MACUpdateHandler
.
ROSTER
)
class
PresenceForecastHandler
(
BaseHandler
):
...
...
@@ -352,7 +361,56 @@ class MessagePageHandler(BaseHandler):
class
RTCHandler
(
BaseHandler
):
def
get
(
self
):
now
=
datetime
.
datetime
.
now
()
now
=
datetime
.
now
()
self
.
write
(
now
.
strftime
(
"%Y-%m-%d %H:%M:%S"
))
self
.
finish
()
class
MACUpdateHandler
(
BaseHandler
):
ROSTER
=
[]
def
post
(
self
):
now
=
datetime
.
now
()
remote_ip
=
self
.
request
.
remote_ip
with
session_scope
()
as
session
:
last
=
query
.
get_last_login_attempt
(
session
,
remote_ip
)
if
last
is
None
:
last
=
LoginAttempt
(
None
,
remote_ip
)
persist
(
session
,
last
)
else
:
if
(
now
-
last
.
timestamp
)
<
timedelta
(
seconds
=
options
.
mac_update_interval
):
LOG
.
warning
(
"Too frequent attempts to update, remote IP address is %s"
,
remote_ip
)
raise
HTTPError
(
403
,
"Too frequent"
)
else
:
last
.
timestamp
=
now
persist
(
session
,
last
)
try
:
password
=
self
.
get_argument
(
"password"
)
macs
=
self
.
get_argument
(
"macs"
)
except
MissingArgumentError
:
LOG
.
warning
(
"MAC update received malformed parameters: %s"
,
self
.
request
.
arguments
)
raise
HTTPError
(
400
,
"Bad parameters list"
)
if
not
secure_compare
(
password
,
options
.
mac_update_password
):
LOG
.
warning
(
"Client provided wrong password for MAC update!"
)
raise
HTTPError
(
403
,
"Wrong password"
)
LOG
.
info
(
"Authorized request to update list of checked-in users from IP address %s"
,
remote_ip
)
macs
=
json
.
loads
(
macs
)
with
session_scope
()
as
session
:
names
=
session
.
\
query
(
distinct
(
User
.
name
)).
\
filter
(
User
.
userid
==
MACToUser
.
userid
).
\
filter
(
MACToUser
.
mac_hash
.
in_
(
macs
)).
\
all
()
MACUpdateHandler
.
ROSTER
=
[
n
[
0
]
for
n
in
names
]
LOG
.
debug
(
"Updated list of checked in users: %s"
,
MACUpdateHandler
.
ROSTER
)
def
check_xsrf_cookie
(
self
):
# Since this is an API call, we need to disable anti-XSRF protection
pass
\ No newline at end of file
bitsd/server/templates/admin.html
View file @
c5fefeb0
...
...
@@ -21,6 +21,18 @@
<input
type=
"submit"
value=
"Cambia stato sede"
>
</p>
</form>
<div
id=
"roster"
>
<h2>
Utenti in sede
</h2>
{% if roster %}
<ul>
{% for name in roster %}
<li>
{{name}}
</li>
{% end %}
</ul>
{% else %}
<p>
Nessuno...
</p>
{% end %}
</div>
<ul
class=
"link"
>
<li><a
id=
"logout"
href=
"/logout"
>
Logout
</a></li>
<li><a
id=
"messages"
href=
"/message"
>
Invia messaggio
</a></li>
...
...
bitsd/server/uimodules.py
View file @
c5fefeb0
...
...
@@ -26,7 +26,7 @@ class DebugMode(tornado.web.UIModule):
class
BasePage
(
tornado
.
web
.
UIModule
):
"""Module providing base css, ico files for all pages and encoding tag."""
def
css_files
(
self
):
css
=
[
'/static/default.css?v=
2
'
,]
css
=
[
'/static/default.css?v=
3
'
,]
# FIXME daltonism workaround, should be implemented client-side
if
'blind'
in
self
.
request
.
path
:
css
.
append
(
'/static/dalton.css?v=1'
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a 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