Commit 6fc932f
Changed files (13)
static/ffu.css
@@ -1,5 +1,6 @@
body {
padding-top: 60px;
+ padding-bottom: 40px;
}
#form-upload {
padding: 15px;
@@ -13,8 +14,43 @@ body {
line-height: 55px;
}
.top-padding {
- margin-top:20px;
+ margin-top:10px;
}
.bot-padding {
- margin-bottom:20px;
+ margin-bottom:10px;
+}
+
+.form-signin {
+ max-width: 330px;
+ padding: 15px;
+ margin: 0 auto;
+}
+.form-signin .form-signin-heading,
+.form-signin .checkbox {
+ margin-bottom: 10px;
+}
+.form-signin .checkbox {
+ font-weight: normal;
+}
+.form-signin .form-control {
+ position: relative;
+ font-size: 16px;
+ height: auto;
+ padding: 10px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.form-signin .form-control:focus {
+ z-index: 2;
+}
+.form-signin input[type="text"] {
+ margin-bottom: -1px;
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+}
+.form-signin input[type="password"] {
+ margin-bottom: 10px;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
}
static/signin.css
@@ -1,39 +0,0 @@
-body {
- padding-top: 60px;
- padding-bottom: 40px;
-}
-
-.form-signin {
- max-width: 330px;
- padding: 15px;
- margin: 0 auto;
-}
-.form-signin .form-signin-heading,
-.form-signin .checkbox {
- margin-bottom: 10px;
-}
-.form-signin .checkbox {
- font-weight: normal;
-}
-.form-signin .form-control {
- position: relative;
- font-size: 16px;
- height: auto;
- padding: 10px;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
-}
-.form-signin .form-control:focus {
- z-index: 2;
-}
-.form-signin input[type="text"] {
- margin-bottom: -1px;
- border-bottom-left-radius: 0;
- border-bottom-right-radius: 0;
-}
-.form-signin input[type="password"] {
- margin-bottom: 10px;
- border-top-left-radius: 0;
- border-top-right-radius: 0;
-}
templates/admin.html
@@ -0,0 +1,11 @@
+{% extends "base-nav.html" %}
+
+{% block css %}
+<link rel="stylesheet" href="{{ static_url("ffu.css") }}">
+{% end %}
+
+{% block content %}
+<div class="container">
+ Admin'y things here
+</div><!-- /.container -->
+{% end %}
templates/base.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="">
+ <meta name="author" content="">
+ <link rel="shortcut icon" href="{{ static_url("images/favicon.ico")}}">
+ <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
+ {% block css %}{% end %}
+</head>
+
+<body>
+ {% block nav %}{% end %}
+ {% block content %}{% end %}
+
+ <script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
+ <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ {% block js %}{% end %}
+</body>
templates/index.html
@@ -0,0 +1,33 @@
+{% extends "base-nav.html" %}
+
+{% block css %}
+<link rel="stylesheet" href="{{ static_url("ffu.css") }}">
+{% end %}
+
+{% block content %}
+<div class="container">
+ <form id="form-upload" enctype="multipart/form-data" action="" method="post">
+ <input type="file" id="fileselect" name="fileselect" multiple="multiple" style="display:none"/>
+ <div class="row">
+ <div class="col-md-8 col-sm-8 bot-padding">
+ <div id="upload-zone" class="well upload-zone">
+ <p id="upload-text" class="lead text-center text-muted">Drop files or Click here</p>
+ </div>
+ </div>
+ <div class="col-md-4 col-sm-4 bot-padding">
+ <button class="btn btn-lg btn-default btn-block" type="submit">Upload</button>
+ <button class="btn btn-lg btn-default btn-block" type="reset">Clear</button>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-xs-12">
+ <ul id="filelist" class="list-group"></ul>
+ </div>
+ </div>
+ </form>
+</div><!-- /.container -->
+{% end %}
+
+{% block js %}
+<script src="{{ static_url("ffu.js") }}"></script>
+{% end %}
templates/login.html
@@ -0,0 +1,21 @@
+{% extends "base-nav.html" %}
+
+{% block css %}
+<link rel="stylesheet" href="{{ static_url("ffu.css") }}">
+{% end %}
+
+{% block content %}
+<div class="container">
+ <form class="form-signin" role="form" action="" method="post">
+ <input type="text" class="form-control" name="username" placeholder="Username" required autofocus>
+ <input type="password" class="form-control" name="password" placeholder="Password" required>
+ <!--<label class="checkbox">
+ <input type="checkbox" value="remember-me"> Remember me
+ </label>-->
+ {% if errormsg is not None %}
+ <div class="alert-warning text-center bot-padding">{{ errormsg }}</div>
+ {% end %}
+ <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
+ </form>
+</div> <!-- /container -->
+{% end %}
.gitignore
@@ -0,0 +1,1 @@
+config.py
ffu.py
@@ -7,9 +7,12 @@ import base64
from StringIO import StringIO
import os
+from login import login
+
class BaseHandler(tornado.web.RequestHandler):
def get_login_url(self):
return u"/login"
+
def get_current_user(self):
user_json = self.get_secure_cookie("user")
if user_json:
@@ -17,62 +20,100 @@ class BaseHandler(tornado.web.RequestHandler):
else:
return None
+ def get_admin_status(self):
+ admin_json = self.get_secure_cookie("admin")
+ if admin_json:
+ return tornado.escape.json_decode(admin_json)
+ else:
+ return None
+
+ def render(self, template, **kwargs):
+ if hasattr(self, 'errormsg'):
+ kwargs['errormsg'] = self.errormsg
+ else:
+ kwargs['errormsg'] = None
+ kwargs['admin'] = self.get_admin_status()
+ super(BaseHandler, self).render(template, **kwargs)
+
class LoginHandler(BaseHandler):
def get(self):
- self.render('login.html', next=self.get_argument("next","/"))
+ self.render('login.html')
+
def post(self):
- print self
- print vars(self)
username = self.get_argument("username", "")
password = self.get_argument("password", "")
- print "login: ",username," ",password
- auth = False
- if username == "user" and password == "pass":
- auth = True
- if auth:
+ status, error = login(username, password)
+ status = ((username=="user" or username=="userb") and password=="pass")
+ if status:
+ adminstatus = error #Yes, ghetto
+ adminstatus = (username=="userb")
self.set_current_user(username)
- self.redirect(self.get_argument("next","/"))
+ self.set_admin_status(adminstatus)
+ self.redirect(self.get_argument("next",u"/"))
else:
- error_msg = u"?error=" + tornado.escape.url_escape("Login Failed")
- self.redirect(u"/login" + error_msg)
+ self.errormsg = error
+ self.render("login.html", errormsg=self.errormsg)
+
def set_current_user(self, user):
if user:
self.set_secure_cookie("user", tornado.escape.json_encode(user))
else:
self.clear_cookie("user")
+ def set_admin_status(self, admin):
+ if admin:
+ self.set_secure_cookie("admin", tornado.escape.json_encode(admin))
+ else:
+ self.clear_cookie("admin")
+
class LogoutHandler(BaseHandler):
def get(self):
self.clear_cookie("user")
+ self.clear_cookie("admin")
self.redirect(u"/login")
-class UploadHandler(tornado.web.RequestHandler):
+class UploadHandler(BaseHandler):
+ @tornado.web.authenticated
def get(self):
self.render("index.html")
+ #username = self.current_user, admin = self.get_admin_status())
+ @tornado.web.authenticated
def post(self):
form_name = 'fileselect'
upload_dir = 'uploads/'
if self.request.files:
-
+
+ # replace with call to actual validation and tar'ing
tar = tarfile.open(upload_dir + str(uuid.uuid4()) + ".tar.gz", "w:gz")
fileinfo = self.request.files.itervalues()
for f in fileinfo:
- #print f[0]['filename'], f[0]['content_type'], len(f[0]['body'])
f_info = tarfile.TarInfo(name=f[0]['filename'])
f_info.size = len(f[0]['body'])
tar.addfile(f_info, StringIO(f[0]['body']))
tar.close()
+class AdminHandler(BaseHandler):
+ @tornado.web.authenticated
+ def get(self):
+ if self.get_admin_status():
+ self.render("admin.html")
+ #username = self.current_user, admin = self.get_admin_status())
+ else:
+ self.redirect(u"/")
+
handlers = [
(r"/", UploadHandler),
+ (r"/admin", AdminHandler),
(r"/login", LoginHandler),
(r"/logout", LogoutHandler),
]
settings = {
- "static_path": os.path.join(os.path.dirname(__file__),"static"),
+ # set cookie_secret to hard coded string for production, currently breaks existing cookies on restart
"cookie_secret": base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes),
+ "static_path": os.path.join(os.path.dirname(__file__),"static"),
+ "template_path": os.path.join(os.path.dirname(__file__),"templates"),
}
application = tornado.web.Application(handlers, **settings )
index.html
@@ -1,59 +0,0 @@
-<!DOCTYPE html>
-<head>
- <meta charset="utf-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta name="description" content="">
- <meta name="author" content="">
- <link rel="shortcut icon" href="{{ static_url("images/favicon.ico")}}">
- <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
- <link rel="stylesheet" href="{{ static_url("ffu.css") }}">
-</head>
-
-<body>
- <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
- <div class="container">
- <div class="navbar-header">
- <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
- <span class="sr-only">Toggle navigation</span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- </button>
- <a class="navbar-brand" href="#">FFU</a>
- </div>
- <div class="collapse navbar-collapse">
- <ul class="nav navbar-nav">
- <li class="active"><a href="#">Home</a></li>
- <li><a href="#about">Admin</a></li>
- </ul>
- </div><!--/.nav-collapse -->
- </div>
- </div>
-
- <div class="container">
- <form id="form-upload" enctype="multipart/form-data" action="" method="post">
- <input type="file" id="fileselect" name="fileselect" multiple="multiple" style="display:none"/>
- <div class="row">
- <div class="col-md-8 col-sm-8 bot-padding">
- <div id="upload-zone" class="well upload-zone">
- <p id="upload-text" class="lead text-center text-muted">Drop files or Click here</p>
- </div>
- </div>
- <div class="col-md-4 col-sm-4 bot-padding">
- <button class="btn btn-lg btn-default btn-block" type="submit">Upload</button>
- <button class="btn btn-lg btn-default btn-block" type="reset">Clear</button>
- </div>
- </div>
- <div class="row">
- <div class="col-xs-12">
- <ul id="filelist" class="list-group"></ul>
- </div>
- </div>
- </form>
- </div><!-- /.container -->
-
- <script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
- <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
- <script src="{{ static_url("ffu.js") }}"></script>
-</body>
login.html
@@ -1,47 +0,0 @@
-<!DOCTYPE html>
-<head>
- <meta charset="utf-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta name="description" content="">
- <meta name="author" content="">
- <link rel="shortcut icon" href="{{ static_url("images/favicon.ico")}}">
- <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
- <link rel="stylesheet" href="{{ static_url("signin.css") }}">
-</head>
-
-<body>
- <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
- <div class="container">
- <div class="navbar-header">
- <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
- <span class="sr-only">Toggle navigation</span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- </button>
- <a class="navbar-brand" href="#">FFU</a>
- </div>
- <div class="collapse navbar-collapse">
- <ul class="nav navbar-nav">
- <li class="active"><a href="#">Home</a></li>
- <li><a href="#about">Admin</a></li>
- </ul>
- </div><!--/.nav-collapse -->
- </div>
- </div>
-
- <div class="container">
- <form class="form-signin" role="form" action="" method="post">
- <input type="text" class="form-control" name="username" placeholder="Username" required autofocus>
- <input type="password" class="form-control" name="password" placeholder="Password" required>
- <!--<label class="checkbox">
- <input type="checkbox" value="remember-me"> Remember me
- </label>-->
- <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
- </form>
- </div> <!-- /container -->
- <script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
- <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
- <script src="{{ static_url("ffu.js") }}"></script>
-</body>
login.py
@@ -0,0 +1,42 @@
+import ldap
+from config import LDAP_SERVER, LDAP_DOMAIN, LDAP_TRANSFER_GROUP, LDAP_ADMIN_GROUP, LDAP_BASE_DN
+
+def login(username, password):
+ """Verifies credentials for username and password.
+ Returns None on success or a string describing the error on failure
+ # Adapt to your needs
+ """
+ # fully qualified AD user name
+ LDAP_USERNAME = '%s@%s' % (username, LDAP_DOMAIN)
+ # your password
+ LDAP_PASSWORD = password
+ ldap_filter = 'userPrincipalName=%s@%s' % (username, LDAP_DOMAIN)
+ attrs = ['memberOf']
+ try:
+ # build a client
+ ldap_client = ldap.initialize(LDAP_SERVER)
+ # perform a synchronous bind
+ ldap_client.set_option(ldap.OPT_REFERRALS,0)
+ ldap_client.simple_bind_s(LDAP_USERNAME, LDAP_PASSWORD)
+ except ldap.INVALID_CREDENTIALS:
+ ldap_client.unbind()
+ return False, 'Wrong username or password'
+ except ldap.SERVER_DOWN:
+ return False, 'AD server not awailable'
+ # all is well so far
+ # get all user groups and store it in cerrypy session for future use
+ groups = str(ldap_client.search_s(LDAP_BASE_DN,
+ ldap.SCOPE_SUBTREE, ldap_filter, attrs)[0][1]['memberOf'])
+ ldap_client.unbind()
+ if LDAP_ADMIN_GROUP in groups:
+ return True, True #Admin
+ elif LDAP_TRANSFER_GROUP in groups:
+ return True, False #Not admin
+ else:
+ return False, 'Not in transfer group'
+
+if __name__ == "__main__":
+ import getpass
+ username = raw_input("Username: ")
+ password = getpass.getpass("Password: ")
+ print login(username, password)
README.md
@@ -1,8 +1,6 @@
-TODO Auth
-TODO add clear button
-TODO move upload up with upload_zone
+TODO Auth -> better way to do error message
+TODO add clear button -> diable until needed
TODO add X button area on list elements to remove each
TODO (python) send success message
TODO file name collisions deduplication
-TODO Disable Updload / Clear when appropriate