Screencast: Session Hijacking

Wenn nach einer Benutzer-Authentifizierung die zugehörigen Cookies über eine unsichere Verbindung ausgetauscht werden, kann dies das Ziel für Session-Hijacking-Angriffe werden. Was in einem solchen Fall getan werden muss und wie dies vermieden werden kann zeigt Ryan in diesem Screencast.

 

Downloads in verschiedenen Formaten:

mp4
m4v
webm
ogg

 

Resourcen:

terminal

[bash]
sudo tcpdump -i lo0 -A
curl http://todo.dev/
curl http://todo.dev/ -H ‚Cookies: …‘
curl https://todo.dev/ -k -H ‚Cookies: …‘
[/bash]

config/environments/production.rb

[ruby]
config.force_ssl = true
[/ruby]

sessions_controller.rb

[ruby]
cookies.signed[:secure_user_id] = {secure: true, value: "secure#{user.id}"}
# …
cookies.delete(:secure_user_id)
[/ruby]

application_controller.rb

[ruby]
def current_user
if !request.ssl? || cookies.signed[:secure_user_id] == "secure#{session[:user_id]}"
@current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
[/ruby]

Screencast: Squeel

Squeel bietet eine umfangreiche DSL an um SQL Abfrage in Ruby zu erstellen. Es baut auf ARel auf und bietet somit auch Zugriff auf dessen Features.

 

Downloads in verschiedenen Formaten:

mp4
m4v
webm
ogg

 

Resourcen:

terminal

[bash]
rails g squeel:initializer
rails c
[/bash]

rails console

[ruby]
Product.where{released_at < 3.months.ago}
Product.where{released_at.lt 3.months.ago}
Product.where{released_at.lt(3.months.ago) & price.gt(20)}
Product.where{released_at.lt(3.months.ago) | price.gt(20)}
[/ruby]

models/product.rb

[ruby]
def self.search(query)
where do
(released_at <= Time.zone.now) &
((discontinued_at == nil) | (discontinued_at > Time.zone.now)) &
(stock >= my{low_stock}) & (name =~ "%#{query}%")
end
end

def self.low_stock
2
end
[/ruby]

Screencast: APIs schützen

Es gibt viele Möglichkeiten den Zugriff auf APIs zu schützen. In diesem Screencast zeigt Ryan erst einen möglichen Schutz über HTTP Basic Auth und später über Tokens die als Parameter übertragen werden.

 

Downloads in verschiedenen Formaten:

mp4
mp4
webm
ogg

 

Resourcen:

terminal

[bash]
rails g model api_key access_token
curl http://localhost:3000/api/products -I
curl http://localhost:3000/api/products -u ‚admin:secret‘
curl ‚http://localhost:3000/api/products?access_token=123‘ -I
curl http://localhost:3000/api/products -H ‚Authorization: Token token="c576f0136149a2e2d9127b3901015545"‘
[/bash]

api/v1/products_controller.rb

[ruby]
# http_basic_authenticate_with name: "admin", password: "secret"
before_filter :restrict_access

private

# def restrict_access
# api_key = ApiKey.find_by_access_token(params[:access_token])
# head :unauthorized unless api_key
# end

def restrict_access
authenticate_or_request_with_http_token do |token, options|
ApiKey.exists?(access_token: token)
end
end
[/ruby]

models/api_key.rb

[ruby]
before_create :generate_access_token

private

def generate_access_token
begin
self.access_token = SecureRandom.hex
end while self.class.exists?(access_token: access_token)
end
[/ruby]

Screencast: Versionierung von REST APIs

APIs sollten konsistent sein. Dies ist allerdings schwer zu bewerkstelligen wenn verschiedene Formate unterstützt werden und/oder die APIs sich weiterentwickeln. In diesem Screencast zeigt Ryan wie eine API-Versionierung über die URL oder HTPP-Header realisiert werden können.

 

Downloads in verschiedenen Formaten:

mp4
m4v
webm
ogg

 

Resourcen:

terminal

[bash]
rails g migration change_products_released_on
rake db:migrate
cp -R app/controllers/api/v1 app/controllers/api/v2
curl -H ‚Accept: application/vnd.example.v1‘ http://localhost:3000/api/products
[/bash]

routes.rb

[ruby]
require ‚api_constraints‘

Store::Application.routes.draw do
namespace :api, defaults: {format: ‚json‘} do
scope module: :v1, constraints: ApiConstraints.new(version: 1) do
resources :products
end
scope module: :v2, constraints: ApiConstraints.new(version: 2, default: true) do
resources :products
end
end

resources :products
root to: ‚products#index‘
end
[/ruby]

lib/api_constraints.rb

[ruby]
class ApiConstraints
def initialize(options)
@version = options[:version]
@default = options[:default]
end

def matches?(req)
@default || req.headers[‚Accept‘].include?("application/vnd.example.v#{@version}")
end
end
[/ruby]

app/controllers/api/v1/products_controller.rb

[ruby]
module Api
module V1
class ProductsController < ApplicationController
class Product < ::Product
# Note: this does not take into consideration the create/update actions for changing released_on
def as_json(options = {})
super.merge(released_on: released_at.to_date)
end
end

respond_to :json

def index
respond_with Product.all
end

def show
respond_with Product.find(params[:id])
end

def create
respond_with Product.create(params[:product])
end

def update
respond_with Product.update(params[:id], params[:product])
end

def destroy
respond_with Product.destroy(params[:id])
end
end
end
end
[/ruby]

Screencast: Das Rails API Gem

Applikationen die eine reine API zur Verfügung erstellen kommen immer häufiger vor und haben einen anderen Anspruch als konventionelle Web-Applikationen. Sie benötigen in der Regel keine/andere Views um Daten zur Verfügung zu stellen. In Rails kann für Applikationen die hauptsächlich APIs anbieten das Rails API Gem eingesetzt werden. Ryan zeigt in dieser Woche wie es verwendet werden kann.

 

Downloads in verschiedenen Formaten:

mp4
m4v
webm
ogg

 

Resourcen:

terminal

[bash]
gem install rails-api
rbenv rehash
rails-api new todo
rails g scaffold task name
rake middleware
diff api_middleware.txt full_middleware.txt
[/bash]

tasks_controller.rb

[ruby]
include ActionController::MimeResponds
include ActionController::Helpers
include ActionController::Cookies
include ActionController::ImplicitRender

# GET /tasks
# GET /tasks.json
def index
@tasks = Task.all

# respond_to do |format|
# format.json { render }
# format.xml { render xml: @tasks }
# end
end
[/ruby]

views/tasks/index.json.rabl

[javascript]
collection @tasks
attributes :id, :name
[/javascript]

config/application.rb

[ruby]
config.middleware.insert_after ActiveRecord::QueryCache, ActionDispatch::Cookies
[/ruby]

Screencast: Mehrseitige Formulare mit Wicked

Mehrseitige Formulare sind nicht immer einfach zu erstellen. Wicket ist ein Plugin das genau diese Nische bedient und die Erstellung vereinfacht. Mit Wicket lassen sich Controller in eine Serie von Schritten verwandeln um seitenweise Daten zu erfassen.

 

Downloads in verschiedenen Formaten:

mp4
m4v
webm
ogg

 

Resourcen:

terminal

[bash]
rails g controller user_steps
[/bash]

user_steps_controller.rb

[ruby]
class UserStepsController < ApplicationController
include Wicked::Wizard
steps :personal, :social

def show
@user = current_user
render_wizard
end

def update
@user = current_user
@user.attributes = params[:user]
render_wizard @user
end

private

def redirect_to_finish_wizard
redirect_to root_url, notice: "Thank you for signing up."
end
end
[/ruby]

user_steps/personal.html.erb

[html]
<h1>Tell us a little about yourself.</h1>

<%= render layout: ‚form‘ do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :date_of_birth %><br />
<%= f.date_select :date_of_birth, start_year: 1900, end_year: Date.today.year, include_blank: true %>
</div>
<div class="field">
<%= f.label :bio %><br />
<%= f.text_area :bio, rows: 5 %>
</div>
<% end %>
[/html]

user_steps/social.html.erb

[html]
<h1>Where can we find you?</h1>

<%= render layout: ‚form‘ do |f| %>
<div class="field">
<%= f.label :twitter_username %><br />
<%= f.text_field :twitter_username %>
</div>
<div class="field">
<%= f.label :github_username %><br />
<%= f.text_field :github_username %>
</div>
<div class="field">
<%= f.label :website %><br />
<%= f.text_field :website %>
</div>
<% end %>
[/html]

user_steps/_form.html.erb

[html]
<%= form_for @user, url: wizard_path do |f| %>
<% if @user.errors.any? %>
<div class="error_messages">
<h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% @user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= yield f %>

<div class="actions">
<%= f.submit "Continue" %>
or <%= link_to "skip this step", next_wizard_path %>
</div>
<% end %>
[/html]

users/new.html.erb

[html]
<h1>Sign Up</h1>

<%= form_for @user do |f| %>
<% if @user.errors.any? %>
<div class="error_messages">
<h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% @user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>

<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</div>

<div class="actions">
<%= f.submit "Sign Up" %>
</div>
<% end %>
[/html]

users_controller.rb

[ruby]
def create
@user = User.new(params[:user])
if @user.save
session[:user_id] = @user.id
redirect_to user_steps_path
else
render :new
end
end
[/ruby]

models/user.rb

[ruby]
validates_format_of :twitter_username, without: /W/, allow_blank: true
# validates_presence_of :twitter_username, if: :on_social_step?
[/ruby]

Screencast: Queue Classic

Nachdem letzte Woche PostgreSQL allgemein vorgestellt wurde, geht es diese Woche weiter mit weiteren Features für die PostgreSQL benutzt werden kann. Neben der normal Datenbank-Funktionalität kann PostgreSQL auch als Queue für die Prozess-Verarbeitung eingesetzt werden. Somit wäre es nicht nötig noch eine zusätzliche Komponente für Hintergrundprozesse zu installieren und zu verwalten. Ryan zeigt diese Woche wie dies alles mit queue_classic gelöst werden kann.

 

Downloads in verschiedenen Formaten:

mp4
m4v
webm
ogg

 

Resourcen:

config/application.rb

[ruby]
# Although not shown in the episode, it is a good idea to uncomment this line in config/application.rb
# This is because the schema we are generating here cannot be represented in Ruby.
config.active_record.schema_format = :sql
[/ruby]

terminal

[bash]
rails g migration add_queue_classic
rails g migration setup_queue_classic
rake db:migrate
rake qc:work
rails c
[/bash]

rails console

[ruby]
QC.enqueue "puts", "hello world"
QC.enqueue "puts", msg: "hello world"
QC.enqueue "puts", "msg" => "hello world"
[/ruby]

Gemfile

[ruby]
gem ‚queue_classic‘, ‚2.0.0rc12‘
[/ruby]

lib/tasks/queue_classic.rake

[ruby]
require "queue_classic"
require "queue_classic/tasks"
[/ruby]

config/initializers/queue_classic.rb

[ruby]
ENV["DATABASE_URL"] = "postgres://localhost/mailer_development"
[/ruby]

migrations/*add_queue_classic.rb

[ruby]
def up
create_table :queue_classic_jobs do |t|
t.string :q_name
t.string :method
t.text :args
t.timestamp :locked_at
end
add_index :queue_classic_jobs, :id
end

def down
drop_table :queue_classic_jobs
end
[/ruby]

migrations/*setup_queue_classic.rb

[ruby]
def up
QC::Queries.load_functions
end

def down
QC::Queries.drop_functions
end
[/ruby]

newsletters_controller.rb

[ruby]
QC.enqueue "Newsletter.deliver", params[:id]
[/ruby]

models/newsletter.rb

[ruby]
def self.deliver(id)
newsletter = find(id)
# raise "Oops"
sleep 10 # simulate long newsletter delivery
newsletter.update_attribute(:delivered_at, Time.zone.now)
end
[/ruby]

Screencast: Migration nach PostgreSQL

PostgreSQL ist eine objekt-orientierte open source Datenbank. Sie ist zwar nicht so weit verbreitet wie MySQL stellt jedoch eine interessante Alternative im Enterprise-Bereich. Ryan zeigt in diesem Screencast wie er von SQLite nach PostgreSQL wechselt und welche Dinge man dabei beachten muss.

 

Downloads in verschiedenen Formaten:

mp4
m4v
webm
ogg

 

Resourcen:

terminal

[bash]
psql –version
brew install postgresql
initdb /usr/local/var/postgres
pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/server.log start
which psql
rails new blog -d postgresql
rake db:create:all
rails g scaffold article name content:text
rake db:migrate
psql blog_development
rails db
gem install taps
taps
taps server sqlite://db/development.sqlite3 rbates secret
taps pull postgres://rbates@localhost/store_development http://rbates:secret@localhost:5000
[/bash]

rails db

[ruby]
select * from articles;
d
d articles
?
h
h select
q
[/ruby]

config/database.yml

[text]
development:
adapter: postgresql
encoding: unicode
database: blog_development
pool: 5
username: rbates
password:

test:
adapter: postgresql
encoding: unicode
database: blog_test
pool: 5
username: rbates
password:
[/text]

Gemfile

[ruby]
gem "pg"
[/ruby]

Screencast: DataTables

DataTables erleichtert die Erstellung von Tabellen in HTML und unterstützt dabei seitenweise Navigation, Sortierung und Suchen. Ryan zeigt in diesem Screencast wie es in Rails eingesetzt werden kann.

 

Downloads in verschiedenen Formaten:

mp4
m4v
webm
ogg

 

Resourcen:

Gemfile

[ruby]
group :assets do
gem ‚jquery-datatables-rails‘, github: ‚rweng/jquery-datatables-rails‘
gem ‚jquery-ui-rails‘
end

gem ‚will_paginate‘
[/ruby]

app/assets/javascripts/application.js

[javascript]
//= require dataTables/jquery.dataTables
[/javascript]

app/assets/stylesheets/application.css

[css]
/*
*= require jquery.ui.core
*= require jquery.ui.theme
*= require dataTables/src/demo_table_jui
*/
[/css]

app/assets/javascripts/products.js.coffee

[javascript]
jQuery ->
$(‚#products‘).dataTable
sPaginationType: "full_numbers"
bJQueryUI: true
bProcessing: true
bServerSide: true
sAjaxSource: $(‚#products‘).data(’source‘)
[/javascript]

views/products/index.html.erb

[html]
<table id="products" class="display" data-source="<%= products_url(format: "json") %>">
<thead>
<tr>
<th>Product Name</th>
<th>Category</th>
<th>Release Date</th>
<th>Price</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
[/html]

products_controller.rb

[ruby]
def index
respond_to do |format|
format.html
format.json { render json: ProductsDatatable.new(view_context) }
end
end
[/ruby]

app/datatables/products_datatable.rb

[ruby]
class ProductsDatatable
delegate :params, :h, :link_to, :number_to_currency, to: :@view

def initialize(view)
@view = view
end

def as_json(options = {})
{
sEcho: params[:sEcho].to_i,
iTotalRecords: Product.count,
iTotalDisplayRecords: products.total_entries,
aaData: data
}
end

private

def data
products.map do |product|
[
link_to(product.name, product),
h(product.category),
h(product.released_on.strftime("%B %e, %Y")),
number_to_currency(product.price)
]
end
end

def products
@products ||= fetch_products
end

def fetch_products
products = Product.order("#{sort_column} #{sort_direction}")
products = products.page(page).per_page(per_page)
if params[:sSearch].present?
products = products.where("name like :search or category like :search", search: "%#{params[:sSearch]}%")
end
products
end

def page
params[:iDisplayStart].to_i/per_page + 1
end

def per_page
params[:iDisplayLength].to_i > 0 ? params[:iDisplayLength].to_i : 10
end

def sort_column
columns = %w[name category released_on price]
columns[params[:iSortCol_0].to_i]
end

def sort_direction
params[:sSortDir_0] == "desc" ? "desc" : "asc"
end
end
[/ruby]

Screencast: Globalize3

Rails bringt bereits standardmäßig die Möglichkeit mit eine mehrsprachige Applikation zu bauen. Dazu werden Textbausteine in Yaml-Dateien erstellt und in der Applikation verwendet. Dies reicht in den meisten Fällen aus, aber ist nur bedingt geeignet um große und dynamisch erstellte Textmengen zu verwalten. Hier hat Globalize3 seine Stärken. Globalize3 ist ein Gem mit welchem mehrsprachige Texte in Datenbanken verwaltet werden kann. Es ist relativ einfach einzubinden und zu verwenden . Ryan zeigt in diesem Screencast wie es funktioniert.

 

Downloads in verschiedenen Formaten:

mp4
m4v
webm
ogg

 

Resourcen:

Gemfile

[ruby]
gem ‚globalize3‘
[/ruby]

terminal

[bash]
rails g migration create_article_translations
rake db:migrate
rails c
[/bash]

rails console

[ruby]
I18n.locale
Article.first.name
I18n.locale = :wk
Article.first.update_attribute(:name, "Ahhyya")
I18n.locale = :en
[/ruby]

db/migrate/create_article_translations.rb

[ruby]
class CreateArticleTranslations < ActiveRecord::Migration
def up
Article.create_translation_table!({
name: :string,
content: :text
}, {
migrate_data: true
})
end

def down
Article.drop_translation_table! migrate_data: true
end
end
[/ruby]

models/article.rb

[ruby]
translates :name, :content
[/ruby]

config/application.rb

[ruby]
config.i18n.fallbacks = true
[/ruby]