Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add admin panel.

  • Loading branch information...
commit 22521d89a8b30b2e99930f2a6d7badf040e407a5 1 parent 82868eb
@cyx cyx authored
View
7 app.rb
@@ -17,9 +17,6 @@
Cuba.use Rack::Protection
Cuba.use Rack::Protection::RemoteReferrer
-# We use the more secure PBKDF2 password strategy (iterations = 5000)
-Shield::Password.strategy = Shield::Password::PBKDF2
-
# Configure your default setting in env.sh by overriding MALONE_URL.
Malone.connect(url: Settings::MALONE_URL)
@@ -44,6 +41,10 @@
run Users
end
+ on "admin" do
+ run Admins
+ end
+
on default do
run Guests
end
View
11 models/admin.rb
@@ -0,0 +1,11 @@
+class Admin < Ohm::Model
+ include Shield::Model
+
+ attribute :email
+ attribute :crypted_password
+ unique :email
+
+ def self.fetch(identifier)
+ with(:email, identifier)
+ end
+end
View
80 routes/admins.rb
@@ -0,0 +1,80 @@
+class Admins < Cuba
+ # If we throw a 401, we get redirected to /admin/login.
+ use Shield::Middleware, "/admin/login"
+
+ # We choose a different layout file for all admin views.
+ settings[:layout] = "admin/layout"
+
+ # Syntax sugar.
+ def current_admin
+ authenticated(Admin)
+ end
+
+ # All `mote_vars` gets published to the views. We publish
+ # `current_admin` by default as well.
+ def mote_vars(content)
+ super.merge(current_admin: current_admin)
+ end
+
+ # This is a very simple way to secure your return
+ # URL and prevent a hijacking attack.
+ def assert_return_path(path)
+ return if path.nil? or not path =~ %r{\A/admin[a-z0-9\-/]*\z}i
+
+ return path
+ end
+
+ # The admin handlers are divided into three different cases:
+ #
+ # CASE 1: You hit /admin/login
+ # CASE 2: You're authenticated, and you hit any other /admin/* URL.
+ # CASE 3: You're not authorized, hence you get a 401.
+ #
+ define do
+ # CASE 1: You hit /admin/login
+ on "login" do
+ on get do
+ res.write view("admin/login", title: "Admin Login", username: nil)
+ end
+
+ on post, param("username"), param("password") do |user, pass|
+ if login(Admin, user, pass, req[:remember])
+ session[:success] = "You have successfully logged in."
+ res.redirect(assert_return_path(req[:return]) || "/admin/dashboard")
+ else
+ session[:error] = "Invalid username and/or password combination."
+ res.write view("admin/login", title: "Login", username: user)
+ end
+ end
+
+ on default do
+ session[:error] = "No username and/or password supplied."
+ res.redirect "/admin/login", 303
+ end
+ end
+
+ # CASE 2: You're authenticated, and you hit any other /admin/* URL.
+ on authenticated(Admin) do
+ on "dashboard" do
+ res.write view("admin/dashboard", title: "Dashboard")
+ end
+
+ on "logout" do
+ session[:success] = "You have successfully logged out."
+
+ logout(Admin)
+ res.redirect "/admin/login", 303
+ end
+
+ on root do
+ res.redirect "/admin/dashboard", 303
+ end
+ end
+
+ # CASE 3: You're not authorized, hence you get a 401.
+ on default do
+ res.status = 401
+ res.write "Forbidden"
+ end
+ end
+end
View
7 views/admin/dashboard.mote
@@ -0,0 +1,7 @@
+<div class="page-header">
+ <h1>Admin Dashboard</h1>
+</div>
+
+<p>
+ Hello there!
+</p>
View
65 views/admin/layout.mote
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>{{ title }} | Site Name</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="">
+ <meta name="author" content="">
+
+ <link href="/less/bootstrap.less" rel="stylesheet/less" type="text/css">
+ <style>
+ body {
+ padding-top: 60px; /* 60px to make the container go all the
+ way to the bottom of the topbar */
+ }
+
+ footer {
+ margin-top: 40px;
+ border-top: 1px solid #EEE;
+ }
+ </style>
+ <script src="/js/less.js"></script>
+
+ <!--[if lt IE 9]>
+ <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+ <div class="navbar navbar-fixed-top">
+ <div class="navbar-inner">
+ <div class="container">
+ <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </a>
+ <a class="brand" href="/admin">Admin</a>
+ <div class="nav-collapse">
+ <ul class="nav">
+ % if current_admin
+ <li><a href="/admin/logout">Logout</a></li>
+ % end
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="container">
+ {{ partial "notices", session: session }}
+
+ {{ content }}
+
+ <footer class="footer">
+ <p>
+ <small>
+ This template is brought to you by <a href="http://citrusbyte.com">Citrusbyte</a>.
+ Clone <a href="https://github.com/citrusbyte/cuba-app">this app</a>
+ and get your project started.
+ </small>
+ </p>
+ </footer>
+ </div>
+ </body>
+</html>
View
20 views/admin/login.mote
@@ -0,0 +1,20 @@
+<div class="page-header">
+ <h1>Admin Login</h1>
+</div>
+
+<form class="well" method="post">
+ <label>Email</label>
+ <input type="text" name="username" value="{{ username }}">
+
+ <label>
+ Password
+ </label>
+ <input type="password" name="password" value="">
+
+ <label class="checkbox">
+ <input type="checkbox" name="remember" value="1">
+ Remember me
+ </label>
+
+ <button class="btn" type="submit" name="submit">Login</button>
+</form>
Please sign in to comment.
Something went wrong with that request. Please try again.