├── lib ├── tasks │ ├── .gitkeep │ ├── secret_token.rake │ ├── tokens.rake │ └── cucumber.rake ├── hostname_validator.rb ├── ip_address_validator.rb ├── record_patterns.rb ├── scoped_finders.rb └── hijacker.rb ├── log └── .gitignore ├── public ├── favicon.ico ├── stylesheets │ └── .gitkeep ├── images │ ├── error.png │ ├── help.png │ ├── lock.png │ ├── rails.png │ ├── table.png │ ├── accept.png │ ├── cancel.png │ ├── loading.gif │ ├── user_go.png │ ├── brick_add.png │ ├── brick_edit.png │ ├── brick_go.png │ ├── flag_green.png │ ├── note_add.png │ ├── note_edit.png │ ├── table_add.png │ ├── table_edit.png │ ├── table_save.png │ ├── user_add.png │ ├── user_edit.png │ ├── brick_delete.png │ ├── database_add.png │ ├── exclamation.png │ ├── flag_orange.png │ ├── table_delete.png │ ├── table_error.png │ ├── user_delete.png │ ├── user_suspend.png │ ├── database_delete.png │ ├── database_edit.png │ ├── report_magnify.png │ ├── top-background.png │ └── prototip │ │ └── styles │ │ ├── creamy │ │ ├── close.png │ │ ├── lefttop.png │ │ ├── loader.gif │ │ ├── topleft.png │ │ ├── righttop.png │ │ ├── topmiddle.png │ │ ├── topright.png │ │ ├── bottomleft.png │ │ ├── bottommiddle.png │ │ ├── bottomright.png │ │ ├── close_hover.png │ │ ├── leftbottom.png │ │ ├── leftmiddle.png │ │ ├── rightbottom.png │ │ └── rightmiddle.png │ │ ├── darkgrey │ │ ├── close.png │ │ ├── lefttop.png │ │ ├── loader.gif │ │ ├── topleft.png │ │ ├── bottomleft.png │ │ ├── leftbottom.png │ │ ├── leftmiddle.png │ │ ├── righttop.png │ │ ├── topmiddle.png │ │ ├── topright.png │ │ ├── bottommiddle.png │ │ ├── bottomright.png │ │ ├── close_hover.png │ │ ├── rightbottom.png │ │ └── rightmiddle.png │ │ ├── default │ │ ├── close.png │ │ ├── loader.gif │ │ ├── lefttop.png │ │ ├── righttop.png │ │ ├── topleft.png │ │ ├── topright.png │ │ ├── bottomleft.png │ │ ├── bottomright.png │ │ ├── close_hover.png │ │ ├── leftbottom.png │ │ ├── leftmiddle.png │ │ ├── rightbottom.png │ │ ├── rightmiddle.png │ │ ├── topmiddle.png │ │ └── bottommiddle.png │ │ ├── protoblue │ │ ├── close.png │ │ ├── loader.gif │ │ ├── lefttop.png │ │ ├── righttop.png │ │ ├── topleft.png │ │ ├── topmiddle.png │ │ ├── topright.png │ │ ├── bottomleft.png │ │ ├── bottomright.png │ │ ├── close_hover.png │ │ ├── leftbottom.png │ │ ├── leftmiddle.png │ │ ├── rightbottom.png │ │ ├── rightmiddle.png │ │ └── bottommiddle.png │ │ └── protogrey │ │ ├── close.png │ │ ├── loader.gif │ │ ├── lefttop.png │ │ ├── righttop.png │ │ ├── topleft.png │ │ ├── topmiddle.png │ │ ├── topright.png │ │ ├── bottomleft.png │ │ ├── bottomright.png │ │ ├── close_hover.png │ │ ├── leftbottom.png │ │ ├── leftmiddle.png │ │ ├── rightbottom.png │ │ ├── rightmiddle.png │ │ └── bottommiddle.png ├── robots.txt ├── dispatch.cgi ├── dispatch.rb ├── 422.html ├── 404.html ├── 500.html ├── dispatch.fcgi ├── .htaccess └── javascripts │ └── application.js ├── .rspec ├── .ruby-version ├── vendor └── plugins │ └── .gitkeep ├── app ├── helpers │ ├── users_helper.rb │ ├── records_helper.rb │ ├── reports_helper.rb │ ├── search_helper.rb │ ├── sessions_helper.rb │ ├── dashboard_helper.rb │ ├── templates_helper.rb │ ├── auth_tokens_helper.rb │ ├── macro_steps_helper.rb │ ├── macros_helper.rb │ ├── domains_helper.rb │ ├── audits_helper.rb │ └── application_helper.rb ├── views │ ├── domains │ │ ├── index.json.rabl │ │ ├── show.json.rabl │ │ ├── change_owner.js.erb │ │ ├── create.json.rabl │ │ ├── new.html.haml │ │ ├── update_note.js.erb │ │ ├── apply_macro.json.rabl │ │ ├── apply_macro.html.haml │ │ ├── index.html.haml │ │ ├── _domain.html.haml │ │ ├── edit.html.haml │ │ ├── _record.html.haml │ │ └── _soa_record.html.haml │ ├── users │ │ ├── new.html.haml │ │ ├── edit.html.haml │ │ ├── index.html.haml │ │ ├── show.html.haml │ │ ├── _form.html.haml │ │ └── _user.html.haml │ ├── audits │ │ ├── index.html.haml │ │ ├── _domain_audit.html.haml │ │ ├── _record_audit.html.haml │ │ └── domain.html.haml │ ├── macros │ │ ├── _macro.html.haml │ │ ├── index.html.haml │ │ └── edit.html.haml │ ├── sessions │ │ ├── token.html.haml │ │ └── new.html.erb │ ├── templates │ │ ├── new.html.haml │ │ ├── edit.html.haml │ │ ├── _zone_template.html.haml │ │ ├── index.html.haml │ │ └── _form.html.haml │ ├── user_mailer │ │ ├── activation.html.erb │ │ └── signup_notification.html.erb │ ├── reports │ │ ├── _user.html.haml │ │ ├── view.html.haml │ │ ├── results.html.haml │ │ └── index.html.haml │ ├── devise │ │ ├── mailer │ │ │ ├── confirmation_instructions.html.erb │ │ │ ├── unlock_instructions.html.erb │ │ │ └── reset_password_instructions.html.erb │ │ ├── unlocks │ │ │ └── new.html.erb │ │ ├── passwords │ │ │ ├── new.html.erb │ │ │ └── edit.html.erb │ │ ├── confirmations │ │ │ └── new.html.erb │ │ ├── registrations │ │ │ ├── new.html.erb │ │ │ └── edit.html.erb │ │ └── shared │ │ │ └── _links.erb │ ├── dashboard │ │ └── index.html.haml │ ├── search │ │ └── results.html.haml │ ├── records │ │ ├── update_soa.js.erb │ │ ├── create.js.erb │ │ └── update.js.erb │ ├── macro_steps │ │ ├── update.js.erb │ │ ├── create.js.erb │ │ └── _macro_step.html.haml │ ├── record_templates │ │ ├── update.js.erb │ │ └── create.js.erb │ └── layouts │ │ └── application.html.haml ├── controllers │ ├── sessions_controller.rb │ ├── dashboard_controller.rb │ ├── audits_controller.rb │ ├── application_controller.rb │ ├── templates_controller.rb │ ├── reports_controller.rb │ ├── users_controller.rb │ ├── search_controller.rb │ ├── macros_controller.rb │ ├── record_templates_controller.rb │ ├── macro_steps_controller.rb │ └── records_controller.rb └── models │ ├── user_observer.rb │ ├── sshfp.rb │ ├── ptr.rb │ ├── cname.rb │ ├── loc.rb │ ├── mx.rb │ ├── txt.rb │ ├── a.rb │ ├── spf.rb │ ├── aaaa.rb │ ├── user_mailer.rb │ ├── srv.rb │ ├── ns.rb │ └── macro_step.rb ├── config ├── initializers │ ├── rabl.rb │ ├── fix_audited.rb │ ├── mime_types.rb │ ├── inflections.rb │ ├── backtrace_silencers.rb │ ├── session_store.rb │ └── new_rails_defaults.rb ├── environment.rb ├── boot.rb ├── cucumber.yml ├── database.yml.template ├── routes.rb ├── environments │ ├── cucumber.rb │ ├── development.rb │ ├── test.rb │ └── production.rb ├── locales │ └── devise.en.yml └── application.rb ├── spec ├── models │ ├── loc_spec.rb │ ├── ptr_spec.rb │ ├── spf_spec.rb │ ├── srv_spec.rb │ ├── ns_spec.rb │ ├── cname_spec.rb │ ├── txt_spec.rb │ ├── aaaa_spec.rb │ ├── a_spec.rb │ ├── mx_spec.rb │ └── macro_spec.rb ├── factories │ ├── sign_in_helpers.rb │ ├── auth_token_factory.rb │ ├── macro_factory.rb │ ├── zone_template_factory.rb │ ├── users_factory.rb │ └── domain_factory.rb ├── helpers │ ├── macros_helper_spec.rb │ ├── macro_steps_helper_spec.rb │ └── application_helper_spec.rb ├── controllers │ ├── dashboard_controller_spec.rb │ ├── audits_controller_spec.rb │ ├── sessions_controller_spec.rb │ ├── record_template_controller_spec.rb │ ├── reports_controller_spec.rb │ ├── templates_controller_spec.rb │ └── search_controller_spec.rb ├── views │ ├── domains │ │ ├── apply_macro.html.haml_spec.rb │ │ └── new.html.haml_spec.rb │ ├── templates │ │ ├── edit.html.haml_spec.rb │ │ ├── new.html.haml_spec.rb │ │ └── show.html.haml_spec.rb │ ├── macros │ │ ├── index.html.haml_spec.rb │ │ ├── show.html.haml_spec.rb │ │ └── edit.html.haml_spec.rb │ ├── reports │ │ └── results.html.haml_spec │ ├── macro_steps │ │ ├── create.js.rjs_spec.rb │ │ └── update.js.rjs_spec.rb │ ├── search │ │ └── results.html.haml_spec.rb │ └── audits │ │ └── domain.html.haml_spec.rb └── spec_helper.rb ├── stories ├── all.rb └── helper.rb ├── .gitignore ├── config.ru ├── db └── migrate │ ├── 20130419205056_add_reset_password_sent_at_to_users.rb │ ├── 006_add_zone_owners.rb │ ├── 007_add_notes_to_domains.rb │ ├── 20101114093243_add_comment_to_audits.rb │ ├── 008_users_can_own_zone_templates.rb │ ├── 20130429134229_add_type_master_to_zone_template.rb │ ├── 20101114093245_add_remote_address_to_audits.rb │ ├── 20101114093244_rename_changes_to_audited_changes.rb │ ├── 20090505124622_disallow_null_values_in_macro_steps_positions.rb │ ├── 010_create_auth_tokens.rb │ ├── 20110218154001_rename_parent_to_association.rb │ ├── 001_create_domains.rb │ ├── 005_create_roles.rb │ ├── 002_create_records.rb │ ├── 003_create_templates.rb │ ├── 20081228121040_create_macros.rb │ ├── 004_create_users.rb │ ├── 20120819220815_rename_association_to_associated.rb │ ├── 009_add_audit_support.rb │ ├── 20101118071851_convert_roles_to_columns_on_user.rb │ └── 20110306115116_devise_create_users.rb ├── doc ├── README_FOR_APP └── examples │ └── tokens │ └── read_only.xml.erb ├── test ├── performance │ └── browsing_test.rb └── test_helper.rb ├── Rakefile ├── script ├── rails ├── cucumber └── provision ├── .travis.yml ├── Vagrantfile ├── CREDITS.textile ├── features └── support │ ├── paths.rb │ ├── selectors.rb │ └── env.rb ├── TODO.textile ├── Gemfile ├── LICENSE └── CONVENTIONS.textile /lib/tasks/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /log/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --colour 2 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.1.4 2 | -------------------------------------------------------------------------------- /vendor/plugins/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/stylesheets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/helpers/users_helper.rb: -------------------------------------------------------------------------------- 1 | module UsersHelper 2 | end -------------------------------------------------------------------------------- /app/helpers/records_helper.rb: -------------------------------------------------------------------------------- 1 | module RecordsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/reports_helper.rb: -------------------------------------------------------------------------------- 1 | module ReportsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/search_helper.rb: -------------------------------------------------------------------------------- 1 | module SearchHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | end -------------------------------------------------------------------------------- /app/helpers/dashboard_helper.rb: -------------------------------------------------------------------------------- 1 | module DashboardHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/templates_helper.rb: -------------------------------------------------------------------------------- 1 | module TemplatesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/auth_tokens_helper.rb: -------------------------------------------------------------------------------- 1 | module AuthTokensHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/macro_steps_helper.rb: -------------------------------------------------------------------------------- 1 | module MacroStepsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/views/domains/index.json.rabl: -------------------------------------------------------------------------------- 1 | collection @domains 2 | attributes :id, :name, :type 3 | -------------------------------------------------------------------------------- /app/views/users/new.html.haml: -------------------------------------------------------------------------------- 1 | %h1.underline New user 2 | 3 | = render :partial => 'form' 4 | -------------------------------------------------------------------------------- /app/views/users/edit.html.haml: -------------------------------------------------------------------------------- 1 | %h1.underline Update user 2 | 3 | = render :partial => 'form' 4 | 5 | -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < Devise::SessionsController 2 | end 3 | -------------------------------------------------------------------------------- /config/initializers/rabl.rb: -------------------------------------------------------------------------------- 1 | Rabl.configure do |config| 2 | config.include_json_root = false 3 | end 4 | -------------------------------------------------------------------------------- /public/images/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/error.png -------------------------------------------------------------------------------- /public/images/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/help.png -------------------------------------------------------------------------------- /public/images/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/lock.png -------------------------------------------------------------------------------- /public/images/rails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/rails.png -------------------------------------------------------------------------------- /public/images/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/table.png -------------------------------------------------------------------------------- /spec/models/loc_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe LOC do 4 | it "should be tested" 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/ptr_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe PTR do 4 | it "should have tests" 5 | end 6 | -------------------------------------------------------------------------------- /app/helpers/macros_helper.rb: -------------------------------------------------------------------------------- 1 | module MacrosHelper 2 | 3 | def possible_owners 4 | User.all 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /public/images/accept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/accept.png -------------------------------------------------------------------------------- /public/images/cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/cancel.png -------------------------------------------------------------------------------- /public/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/loading.gif -------------------------------------------------------------------------------- /public/images/user_go.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/user_go.png -------------------------------------------------------------------------------- /spec/models/spf_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe SPF do 4 | pending "should have tests" 5 | end 6 | -------------------------------------------------------------------------------- /app/views/audits/index.html.haml: -------------------------------------------------------------------------------- 1 | %h1.underline= t :title_audit_search_logs 2 | 3 | %em= t :text_audit_comming_soon 4 | -------------------------------------------------------------------------------- /app/views/macros/_macro.html.haml: -------------------------------------------------------------------------------- 1 | %tr 2 | %td 3 | = link_to macro.name, macro_path( macro ) 4 | %td   5 | %td -------------------------------------------------------------------------------- /app/views/sessions/token.html.haml: -------------------------------------------------------------------------------- 1 | %h1= t :title_session_token_expired 2 | 3 | %p= t :text_session_exipired_explain 4 | -------------------------------------------------------------------------------- /app/views/templates/new.html.haml: -------------------------------------------------------------------------------- 1 | %h1.underline= t("title_template_editing.new") 2 | 3 | = render :partial => 'form' 4 | -------------------------------------------------------------------------------- /public/images/brick_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/brick_add.png -------------------------------------------------------------------------------- /public/images/brick_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/brick_edit.png -------------------------------------------------------------------------------- /public/images/brick_go.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/brick_go.png -------------------------------------------------------------------------------- /public/images/flag_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/flag_green.png -------------------------------------------------------------------------------- /public/images/note_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/note_add.png -------------------------------------------------------------------------------- /public/images/note_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/note_edit.png -------------------------------------------------------------------------------- /public/images/table_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/table_add.png -------------------------------------------------------------------------------- /public/images/table_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/table_edit.png -------------------------------------------------------------------------------- /public/images/table_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/table_save.png -------------------------------------------------------------------------------- /public/images/user_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/user_add.png -------------------------------------------------------------------------------- /public/images/user_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/user_edit.png -------------------------------------------------------------------------------- /app/views/templates/edit.html.haml: -------------------------------------------------------------------------------- 1 | %h1.underline= t("title_template_editing.update") 2 | 3 | = render :partial => 'form' 4 | -------------------------------------------------------------------------------- /public/images/brick_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/brick_delete.png -------------------------------------------------------------------------------- /public/images/database_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/database_add.png -------------------------------------------------------------------------------- /public/images/exclamation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/exclamation.png -------------------------------------------------------------------------------- /public/images/flag_orange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/flag_orange.png -------------------------------------------------------------------------------- /public/images/table_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/table_delete.png -------------------------------------------------------------------------------- /public/images/table_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/table_error.png -------------------------------------------------------------------------------- /public/images/user_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/user_delete.png -------------------------------------------------------------------------------- /public/images/user_suspend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/user_suspend.png -------------------------------------------------------------------------------- /app/views/user_mailer/activation.html.erb: -------------------------------------------------------------------------------- 1 | <%= t :text_user_mailer_accout_activated, :user => @user.login %> 2 | 3 | <%= @url %> -------------------------------------------------------------------------------- /public/images/database_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/database_delete.png -------------------------------------------------------------------------------- /public/images/database_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/database_edit.png -------------------------------------------------------------------------------- /public/images/report_magnify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/report_magnify.png -------------------------------------------------------------------------------- /public/images/top-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/top-background.png -------------------------------------------------------------------------------- /stories/all.rb: -------------------------------------------------------------------------------- 1 | dir = File.dirname(__FILE__) 2 | Dir[File.expand_path("#{dir}/**/*.rb")].uniq.each do |file| 3 | require file 4 | end -------------------------------------------------------------------------------- /public/images/prototip/styles/creamy/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/creamy/close.png -------------------------------------------------------------------------------- /public/images/prototip/styles/creamy/lefttop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/creamy/lefttop.png -------------------------------------------------------------------------------- /public/images/prototip/styles/creamy/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/creamy/loader.gif -------------------------------------------------------------------------------- /public/images/prototip/styles/creamy/topleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/creamy/topleft.png -------------------------------------------------------------------------------- /public/images/prototip/styles/darkgrey/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/darkgrey/close.png -------------------------------------------------------------------------------- /public/images/prototip/styles/default/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/default/close.png -------------------------------------------------------------------------------- /public/images/prototip/styles/default/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/default/loader.gif -------------------------------------------------------------------------------- /stories/helper.rb: -------------------------------------------------------------------------------- 1 | ENV["RAILS_ENV"] = "test" 2 | require File.expand_path(File.dirname(__FILE__) + "/../config/environment") 3 | require 'spec/rails/story_adapter' -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle 2 | db/*.sqlite3 3 | log/*.log 4 | *.swp 5 | tmp/* 6 | config/initializers/secret_token.rb 7 | config/database.yml 8 | 9 | /.vagrant 10 | -------------------------------------------------------------------------------- /app/views/reports/_user.html.haml: -------------------------------------------------------------------------------- 1 | %tr 2 | %td= link_to user.login, :action => :view , :id => user 3 | %td.right= user.domains.count 4 | %td= user.email 5 | -------------------------------------------------------------------------------- /public/images/prototip/styles/creamy/righttop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/creamy/righttop.png -------------------------------------------------------------------------------- /public/images/prototip/styles/creamy/topmiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/creamy/topmiddle.png -------------------------------------------------------------------------------- /public/images/prototip/styles/creamy/topright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/creamy/topright.png -------------------------------------------------------------------------------- /public/images/prototip/styles/darkgrey/lefttop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/darkgrey/lefttop.png -------------------------------------------------------------------------------- /public/images/prototip/styles/darkgrey/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/darkgrey/loader.gif -------------------------------------------------------------------------------- /public/images/prototip/styles/darkgrey/topleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/darkgrey/topleft.png -------------------------------------------------------------------------------- /public/images/prototip/styles/default/lefttop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/default/lefttop.png -------------------------------------------------------------------------------- /public/images/prototip/styles/default/righttop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/default/righttop.png -------------------------------------------------------------------------------- /public/images/prototip/styles/default/topleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/default/topleft.png -------------------------------------------------------------------------------- /public/images/prototip/styles/default/topright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/default/topright.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protoblue/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protoblue/close.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protoblue/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protoblue/loader.gif -------------------------------------------------------------------------------- /public/images/prototip/styles/protogrey/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protogrey/close.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protogrey/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protogrey/loader.gif -------------------------------------------------------------------------------- /spec/factories/sign_in_helpers.rb: -------------------------------------------------------------------------------- 1 | module SignInHelpers 2 | def tokenize_as( token ) 3 | @request.session[:token_id] = token ? token.id : nil 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /public/images/prototip/styles/creamy/bottomleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/creamy/bottomleft.png -------------------------------------------------------------------------------- /public/images/prototip/styles/creamy/bottommiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/creamy/bottommiddle.png -------------------------------------------------------------------------------- /public/images/prototip/styles/creamy/bottomright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/creamy/bottomright.png -------------------------------------------------------------------------------- /public/images/prototip/styles/creamy/close_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/creamy/close_hover.png -------------------------------------------------------------------------------- /public/images/prototip/styles/creamy/leftbottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/creamy/leftbottom.png -------------------------------------------------------------------------------- /public/images/prototip/styles/creamy/leftmiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/creamy/leftmiddle.png -------------------------------------------------------------------------------- /public/images/prototip/styles/creamy/rightbottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/creamy/rightbottom.png -------------------------------------------------------------------------------- /public/images/prototip/styles/creamy/rightmiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/creamy/rightmiddle.png -------------------------------------------------------------------------------- /public/images/prototip/styles/darkgrey/bottomleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/darkgrey/bottomleft.png -------------------------------------------------------------------------------- /public/images/prototip/styles/darkgrey/leftbottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/darkgrey/leftbottom.png -------------------------------------------------------------------------------- /public/images/prototip/styles/darkgrey/leftmiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/darkgrey/leftmiddle.png -------------------------------------------------------------------------------- /public/images/prototip/styles/darkgrey/righttop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/darkgrey/righttop.png -------------------------------------------------------------------------------- /public/images/prototip/styles/darkgrey/topmiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/darkgrey/topmiddle.png -------------------------------------------------------------------------------- /public/images/prototip/styles/darkgrey/topright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/darkgrey/topright.png -------------------------------------------------------------------------------- /public/images/prototip/styles/default/bottomleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/default/bottomleft.png -------------------------------------------------------------------------------- /public/images/prototip/styles/default/bottomright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/default/bottomright.png -------------------------------------------------------------------------------- /public/images/prototip/styles/default/close_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/default/close_hover.png -------------------------------------------------------------------------------- /public/images/prototip/styles/default/leftbottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/default/leftbottom.png -------------------------------------------------------------------------------- /public/images/prototip/styles/default/leftmiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/default/leftmiddle.png -------------------------------------------------------------------------------- /public/images/prototip/styles/default/rightbottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/default/rightbottom.png -------------------------------------------------------------------------------- /public/images/prototip/styles/default/rightmiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/default/rightmiddle.png -------------------------------------------------------------------------------- /public/images/prototip/styles/default/topmiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/default/topmiddle.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protoblue/lefttop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protoblue/lefttop.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protoblue/righttop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protoblue/righttop.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protoblue/topleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protoblue/topleft.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protoblue/topmiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protoblue/topmiddle.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protoblue/topright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protoblue/topright.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protogrey/lefttop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protogrey/lefttop.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protogrey/righttop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protogrey/righttop.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protogrey/topleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protogrey/topleft.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protogrey/topmiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protogrey/topmiddle.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protogrey/topright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protogrey/topright.png -------------------------------------------------------------------------------- /public/images/prototip/styles/darkgrey/bottommiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/darkgrey/bottommiddle.png -------------------------------------------------------------------------------- /public/images/prototip/styles/darkgrey/bottomright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/darkgrey/bottomright.png -------------------------------------------------------------------------------- /public/images/prototip/styles/darkgrey/close_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/darkgrey/close_hover.png -------------------------------------------------------------------------------- /public/images/prototip/styles/darkgrey/rightbottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/darkgrey/rightbottom.png -------------------------------------------------------------------------------- /public/images/prototip/styles/darkgrey/rightmiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/darkgrey/rightmiddle.png -------------------------------------------------------------------------------- /public/images/prototip/styles/default/bottommiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/default/bottommiddle.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protoblue/bottomleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protoblue/bottomleft.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protoblue/bottomright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protoblue/bottomright.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protoblue/close_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protoblue/close_hover.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protoblue/leftbottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protoblue/leftbottom.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protoblue/leftmiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protoblue/leftmiddle.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protoblue/rightbottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protoblue/rightbottom.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protoblue/rightmiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protoblue/rightmiddle.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protogrey/bottomleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protogrey/bottomleft.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protogrey/bottomright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protogrey/bottomright.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protogrey/close_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protogrey/close_hover.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protogrey/leftbottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protogrey/leftbottom.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protogrey/leftmiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protogrey/leftmiddle.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protogrey/rightbottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protogrey/rightbottom.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protogrey/rightmiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protogrey/rightmiddle.png -------------------------------------------------------------------------------- /spec/models/srv_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe SRV do 4 | it "should support priorities" do 5 | subject.supports_prio?.should be_true 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/views/domains/show.json.rabl: -------------------------------------------------------------------------------- 1 | object @domain 2 | attributes :id, :name, :type 3 | 4 | child :records => :records do 5 | attributes :id, :name, :type, :content, :prio 6 | end 7 | -------------------------------------------------------------------------------- /public/images/prototip/styles/protoblue/bottommiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protoblue/bottommiddle.png -------------------------------------------------------------------------------- /public/images/prototip/styles/protogrey/bottommiddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethkalmer/powerdns-on-rails/HEAD/public/images/prototip/styles/protogrey/bottommiddle.png -------------------------------------------------------------------------------- /config/initializers/fix_audited.rb: -------------------------------------------------------------------------------- 1 | Audit = Audited::Adapters::ActiveRecord::Audit 2 | #TODO: potential vulnarability 3 | Audit.attr_accessible :username, :user, :version, :auditable 4 | -------------------------------------------------------------------------------- /app/views/domains/change_owner.js.erb: -------------------------------------------------------------------------------- 1 | $('#owner-info').html('<%= escape_javascript(@domain.user.nil? ? "-" : @domain.user.login) %>'); 2 | $('#owner-info').show(); 3 | $('#owner-edit').hide(); -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run PowerdnsOnRails::Application 5 | -------------------------------------------------------------------------------- /app/views/domains/create.json.rabl: -------------------------------------------------------------------------------- 1 | object @domain 2 | 3 | attributes :id, :name, :type, :errors 4 | 5 | child :records => :records do 6 | attributes :id, :name, :type, :content, :prio 7 | end 8 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the rails application 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the rails application 5 | PowerdnsOnRails::Application.initialize! 6 | -------------------------------------------------------------------------------- /app/views/domains/new.html.haml: -------------------------------------------------------------------------------- 1 | %h1.underline= t :title_domain_new_zone 2 | 3 | = render :partial => 'new' 4 | 5 | %p   6 | 7 | %p 8 | = dns_book t(:label_domain_more_soa_info), t(:link_to_soa_help) -------------------------------------------------------------------------------- /app/controllers/dashboard_controller.rb: -------------------------------------------------------------------------------- 1 | class DashboardController < ApplicationController 2 | 3 | def index 4 | @latest_domains = Domain.user(current_user).order('created_at DESC').limit(5) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/views/domains/update_note.js.erb: -------------------------------------------------------------------------------- 1 | $('#note-text').html('<%= escape_javascript @domain.notes %>'); 2 | $('#domain_notes').html('<%= escape_javascript @domain.notes %>'); 3 | $('#note-form').hide(); 4 | $('#note').show(); 5 | -------------------------------------------------------------------------------- /db/migrate/20130419205056_add_reset_password_sent_at_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddResetPasswordSentAtToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :reset_password_sent_at, :datetime 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /doc/README_FOR_APP: -------------------------------------------------------------------------------- 1 | Use this README file to introduce your application and point to useful places in the API for learning more. 2 | Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries. 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-Agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /db/migrate/006_add_zone_owners.rb: -------------------------------------------------------------------------------- 1 | class AddZoneOwners < ActiveRecord::Migration 2 | def self.up 3 | add_column :domains, :user_id, :integer 4 | end 5 | 6 | def self.down 7 | remove_column :domains, :user_id 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | # Mime::Type.register_alias "text/html", :iphone 6 | -------------------------------------------------------------------------------- /db/migrate/007_add_notes_to_domains.rb: -------------------------------------------------------------------------------- 1 | class AddNotesToDomains < ActiveRecord::Migration 2 | def self.up 3 | add_column :domains, :notes, :text 4 | end 5 | 6 | def self.down 7 | remove_column :domains, :notes 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/views/reports/view.html.haml: -------------------------------------------------------------------------------- 1 | %h2.underline 2 | = @user.login 3 | = t :label_user_has 4 | = t :label_lc_domain, :count => @user.domains.count 5 | 6 | #domains.padded 7 | = render :partial => '/domains/domain' , :collection => @user.domains 8 | -------------------------------------------------------------------------------- /db/migrate/20101114093243_add_comment_to_audits.rb: -------------------------------------------------------------------------------- 1 | class AddCommentToAudits < ActiveRecord::Migration 2 | def self.up 3 | add_column :audits, :comment, :string 4 | end 5 | 6 | def self.down 7 | remove_column :audits, :comment 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/008_users_can_own_zone_templates.rb: -------------------------------------------------------------------------------- 1 | class UsersCanOwnZoneTemplates < ActiveRecord::Migration 2 | def self.up 3 | add_column :zone_templates, :user_id, :integer 4 | end 5 | 6 | def self.down 7 | remove_column :zone_templates, :user_id 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20130429134229_add_type_master_to_zone_template.rb: -------------------------------------------------------------------------------- 1 | class AddTypeMasterToZoneTemplate < ActiveRecord::Migration 2 | def change 3 | add_column :zone_templates, :type, :string, :default => 'NATIVE' 4 | add_column :zone_templates, :master, :string 5 | end 6 | end 7 | 8 | -------------------------------------------------------------------------------- /app/views/user_mailer/signup_notification.html.erb: -------------------------------------------------------------------------------- 1 | <%= t :text_user_mailer_account_created%> 2 | 3 | <%= t :label_user_name %>: <%= @user.login %> 4 | <%= t :label_user_password %>: <%= @user.password %> 5 | 6 | <%= t :text_user_mailer_visit_url_to_activate_account %> 7 | 8 | <%= @url %> -------------------------------------------------------------------------------- /app/views/devise/mailer/confirmation_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Welcome <%= @resource.email %>!

2 | 3 |

You can confirm your account through the link below:

4 | 5 |

<%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>

6 | -------------------------------------------------------------------------------- /db/migrate/20101114093245_add_remote_address_to_audits.rb: -------------------------------------------------------------------------------- 1 | class AddRemoteAddressToAudits < ActiveRecord::Migration 2 | def self.up 3 | add_column :audits, :remote_address, :string 4 | end 5 | 6 | def self.down 7 | remove_column :audits, :remote_address 8 | end 9 | end 10 | 11 | -------------------------------------------------------------------------------- /test/performance/browsing_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'rails/performance_test_help' 3 | 4 | # Profiling results for each test method are written to tmp/performance. 5 | class BrowsingTest < ActionDispatch::PerformanceTest 6 | def test_homepage 7 | get '/' 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | require 'rake' 6 | 7 | PowerdnsOnRails::Application.load_tasks 8 | -------------------------------------------------------------------------------- /app/views/dashboard/index.html.haml: -------------------------------------------------------------------------------- 1 | %h1.underline 2 | =t :label_last_x_domains, :value => 5 3 | 4 | %table 5 | = render :partial => '/domains/domain', :collection => @latest_domains 6 | 7 |   8 | %h1.underline 9 | = t :label_new_domain 10 | 11 | = render :partial => '/domains/new' 12 | -------------------------------------------------------------------------------- /app/models/user_observer.rb: -------------------------------------------------------------------------------- 1 | class UserObserver < ActiveRecord::Observer 2 | def after_create(user) 3 | UserMailer.deliver_signup_notification(user) 4 | end 5 | 6 | def after_save(user) 7 | 8 | UserMailer.deliver_activation(user) if user.recently_activated? 9 | 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/hostname_validator.rb: -------------------------------------------------------------------------------- 1 | class HostnameValidator < ActiveModel::EachValidator 2 | include RecordPatterns 3 | 4 | def validate_each( record, attribute, value ) 5 | record.errors[ attribute ] << I18n.t(:message_attribute_must_be_hostname) unless hostname?( value ) && !ip?( value ) 6 | end 7 | 8 | end 9 | -------------------------------------------------------------------------------- /app/views/search/results.html.haml: -------------------------------------------------------------------------------- 1 | %h1.underline= t :title_search_result_for, :who => params[:q] 2 | %br/ 3 | 4 | - if @results.empty? 5 | %strong= t :message_search_domain_not_found 6 | - else 7 | %table 8 | = render :partial => '/domains/domain', :collection => @results 9 | 10 | = will_paginate @results -------------------------------------------------------------------------------- /db/migrate/20101114093244_rename_changes_to_audited_changes.rb: -------------------------------------------------------------------------------- 1 | class RenameChangesToAuditedChanges < ActiveRecord::Migration 2 | def self.up 3 | rename_column :audits, :changes, :audited_changes 4 | end 5 | 6 | def self.down 7 | rename_column :audits, :audited_changes, :changes 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 3 | 4 | APP_PATH = File.expand_path('../../config/application', __FILE__) 5 | require File.expand_path('../../config/boot', __FILE__) 6 | require 'rails/commands' 7 | -------------------------------------------------------------------------------- /spec/models/ns_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe NS do 4 | context "when new" do 5 | it "should be invalid by default" do 6 | subject.should_not be_valid 7 | end 8 | 9 | it "should require content" do 10 | subject.should have(2).error_on(:content) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/helpers/domains_helper.rb: -------------------------------------------------------------------------------- 1 | module DomainsHelper 2 | 3 | def select_record_type( form ) 4 | types = if current_user 5 | RecordTemplate.record_types.map{ |t| [t,t] } - [["SOA", "SOA"]] 6 | else 7 | current_token.new_types 8 | end 9 | 10 | form.select( :type, types ) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/views/audits/_domain_audit.html.haml: -------------------------------------------------------------------------------- 1 | %li 2 | = link_to_domain_audit( domain_audit ) 3 | %div[ domain_audit ]{ :style => 'display: none' } 4 | %strong= t :label_audit_time 5 | = domain_audit.created_at 6 | %br 7 | %strong= t :label_audit_changes 8 | %br 9 | = display_hash domain_audit.audited_changes 10 | -------------------------------------------------------------------------------- /db/migrate/20090505124622_disallow_null_values_in_macro_steps_positions.rb: -------------------------------------------------------------------------------- 1 | class DisallowNullValuesInMacroStepsPositions < ActiveRecord::Migration 2 | def self.up 3 | change_column_null :macro_steps, :position, false 4 | end 5 | 6 | def self.down 7 | change_column_null :macro_steps, :position, true 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/views/audits/_record_audit.html.haml: -------------------------------------------------------------------------------- 1 | %li 2 | = link_to_record_audit( record_audit ) 3 | %div[ record_audit ]{ :style => 'display: none' } 4 | %strong= t :label_audit_time 5 | = record_audit.created_at 6 | %br 7 | %strong= t :label_audit_changes 8 | %br 9 | = display_hash record_audit.audited_changes 10 | 11 | -------------------------------------------------------------------------------- /spec/models/cname_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe CNAME do 4 | 5 | context "when new" do 6 | 7 | it "should be invalid by default" do 8 | subject.should_not be_valid 9 | end 10 | 11 | it "should require content" do 12 | subject.should have(2).error_on(:content) 13 | end 14 | 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /script/cucumber: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | vendored_cucumber_bin = Dir["#{File.dirname(__FILE__)}/../vendor/{gems,plugins}/cucumber*/bin/cucumber"].first 4 | if vendored_cucumber_bin 5 | load File.expand_path(vendored_cucumber_bin) 6 | else 7 | require 'rubygems' unless ENV['NO_RUBYGEMS'] 8 | require 'cucumber' 9 | load Cucumber::BINARY 10 | end 11 | -------------------------------------------------------------------------------- /app/views/devise/mailer/unlock_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Your account has been locked due to an excessive amount of unsuccessful sign in attempts.

4 | 5 |

Click the link below to unlock your account:

6 | 7 |

<%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token) %>

8 | -------------------------------------------------------------------------------- /app/views/domains/apply_macro.json.rabl: -------------------------------------------------------------------------------- 1 | # Has the macro been applied yet? 2 | if @macro.present? 3 | # Yes 4 | object @domain 5 | attributes :id, :name, :type 6 | child :records => :records do 7 | attributes :id, :name, :type, :content, :prio 8 | end 9 | 10 | else 11 | # No 12 | collection @macros 13 | attributes :id, :name 14 | 15 | end 16 | -------------------------------------------------------------------------------- /app/models/sshfp.rb: -------------------------------------------------------------------------------- 1 | # See #SSHFP 2 | 3 | # = SSH Fingerprint (SSHFP) 4 | # 5 | # Defined in RFC 4255. 6 | # 7 | # Including draft extension for SHA2 and ECDSA. See https://tools.ietf.org/html/draft-os-ietf-sshfp-ecdsa-sha2-04 8 | 9 | class SSHFP < Record 10 | 11 | validates_format_of :content, :with => /^[1-3] [1-2] ([0-9a-fA-F]{40}|[0-9a-fA-F]{64})$/ 12 | 13 | end 14 | -------------------------------------------------------------------------------- /lib/ip_address_validator.rb: -------------------------------------------------------------------------------- 1 | class IpAddressValidator < ActiveModel::EachValidator 2 | include RecordPatterns 3 | 4 | def validate_each( record, attribute, value ) 5 | record.errors[ attribute ] << I18n.t(:message_attribute_must_be_ip) unless valid?( value ) 6 | end 7 | 8 | def valid?( ip ) 9 | ( options[:ipv6] && ipv6?( ip ) ) || ipv4?( ip ) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/views/users/index.html.haml: -------------------------------------------------------------------------------- 1 | #new-user{ :style => 'display: none' }= t :help_user_add 2 | 3 | %h2.underline= t :title_users 4 | 5 | %table.gridwide 6 | %tr 7 | %td{ :colspan => 5 } 8 | = link_to info_icon('user_add.png', 'new-user'), new_user_path 9 | = link_to t(:label_user_create), new_user_path 10 | = render :partial => 'user', :collection => @users 11 | 12 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | 3 | # Set up gems listed in the Gemfile. 4 | gemfile = File.expand_path('../../Gemfile', __FILE__) 5 | begin 6 | ENV['BUNDLE_GEMFILE'] = gemfile 7 | require 'bundler' 8 | Bundler.setup 9 | rescue Bundler::GemNotFound => e 10 | STDERR.puts e.message 11 | STDERR.puts "Try running `bundle install`." 12 | exit! 13 | end if File.exist?(gemfile) 14 | -------------------------------------------------------------------------------- /spec/helpers/macros_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe MacrosHelper do 4 | 5 | #Delete this example and add some real ones or delete this file 6 | it "should be included in the object returned by #helper" do 7 | included_modules = (class << helper; self; end).send :included_modules 8 | included_modules.should include(MacrosHelper) 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/audits_controller.rb: -------------------------------------------------------------------------------- 1 | class AuditsController < ApplicationController 2 | 3 | before_filter do 4 | unless current_user.admin? 5 | redirect_to root_url 6 | end 7 | end 8 | 9 | def index 10 | 11 | end 12 | 13 | # Retrieve the audit details for a domain 14 | def domain 15 | @domain = Domain.user( current_user ).find( params[:id] ) 16 | end 17 | 18 | end 19 | -------------------------------------------------------------------------------- /spec/helpers/macro_steps_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe MacroStepsHelper do 4 | 5 | #Delete this example and add some real ones or delete this file 6 | it "should be included in the object returned by #helper" do 7 | included_modules = (class << helper; self; end).send :included_modules 8 | included_modules.should include(MacroStepsHelper) 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /app/models/ptr.rb: -------------------------------------------------------------------------------- 1 | # See #PTR 2 | 3 | # = Name Server Record (PTR) 4 | # 5 | # Pointer records are the opposite of A and AAAA RRs and are used in Reverse Map 6 | # zone files to map an IP address (IPv4 or IPv6) to a host name. 7 | # 8 | # Obtained from http://www.zytrax.com/books/dns/ch8/ptr.html 9 | # 10 | class PTR < Record 11 | 12 | validates :content, :presence => true, :hostname => true 13 | 14 | end 15 | -------------------------------------------------------------------------------- /spec/controllers/dashboard_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe DashboardController, "and admins" do 4 | 5 | before(:each) do 6 | sign_in( FactoryGirl.create(:admin) ) 7 | 8 | FactoryGirl.create(:domain) 9 | 10 | get :index 11 | end 12 | 13 | it "should have a list of the latest zones" do 14 | assigns(:latest_domains).should_not be_empty 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /app/views/records/update_soa.js.erb: -------------------------------------------------------------------------------- 1 | $('#soa').replaceWith('<%= escape_javascript render(:partial => '/domains/soa_record', :object => @domain.soa_record) %>'); 2 | 3 | <% if @domain.soa_record.errors.empty? %> 4 | $('#soa-form').hide(); 5 | $('#soa-view').show(); 6 | humane("<%= escape_javascript t(:message_record_soa_updated) %>"); 7 | <% else %> 8 | $('#soa-form').show(); 9 | $('#soa-view').hide(); 10 | <% end %> 11 | -------------------------------------------------------------------------------- /app/models/cname.rb: -------------------------------------------------------------------------------- 1 | # See #CNAME 2 | 3 | # = Canonical Name Record (CNAME) 4 | # 5 | # A CNAME record maps an alias or nickname to the real or Canonical name which 6 | # may lie outside the current zone. Canonical means expected or real name. 7 | # 8 | # Obtained from http://www.zytrax.com/books/dns/ch8/cname.html 9 | # 10 | class CNAME < Record 11 | 12 | validates :content, :presence => true, :hostname => true 13 | 14 | end 15 | -------------------------------------------------------------------------------- /app/views/reports/results.html.haml: -------------------------------------------------------------------------------- 1 | %h1.underline= t :title_report_result_for, :who => params[:q] 2 | %br/ 3 | 4 | - if @results.empty? 5 | %strong= t :message_user_not_found 6 | - else 7 | %table 8 | %th= t :label_user_login 9 | %th= t :label_user_domains 10 | %th= t :label_user_email 11 | %th= t :label_user_role 12 | = render :partial => 'user', :collection => @results 13 | 14 | = will_paginate @results -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format 4 | # (all these examples are active by default): 5 | # ActiveSupport::Inflector.inflections do |inflect| 6 | # inflect.plural /^(ox)$/i, '\1en' 7 | # inflect.singular /^(ox)en/i, '\1' 8 | # inflect.irregular 'person', 'people' 9 | # inflect.uncountable %w( fish sheep ) 10 | # end 11 | -------------------------------------------------------------------------------- /app/views/devise/unlocks/new.html.erb: -------------------------------------------------------------------------------- 1 |

Resend unlock instructions

2 | 3 | <%= form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |

<%= f.label :email %>
7 | <%= f.email_field :email %>

8 | 9 |

<%= f.submit "Resend unlock instructions" %>

10 | <% end %> 11 | 12 | <%= render :partial => "devise/shared/links" %> -------------------------------------------------------------------------------- /app/views/devise/passwords/new.html.erb: -------------------------------------------------------------------------------- 1 |

Forgot your password?

2 | 3 | <%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |

<%= f.label :email %>
7 | <%= f.email_field :email %>

8 | 9 |

<%= f.submit "Send me reset password instructions" %>

10 | <% end %> 11 | 12 | <%= render :partial => "devise/shared/links" %> -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /db/migrate/010_create_auth_tokens.rb: -------------------------------------------------------------------------------- 1 | class CreateAuthTokens < ActiveRecord::Migration 2 | def self.up 3 | create_table :auth_tokens do |t| 4 | t.references :domain 5 | t.references :user 6 | t.string :token, :null => false 7 | t.text :permissions, :null => false 8 | t.timestamps 9 | t.datetime :expires_at, :null => false 10 | end 11 | end 12 | 13 | def self.down 14 | drop_table :auth_tokens 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /db/migrate/20110218154001_rename_parent_to_association.rb: -------------------------------------------------------------------------------- 1 | class RenameParentToAssociation < ActiveRecord::Migration 2 | def self.up 3 | rename_column :audits, :auditable_parent_id, :association_id 4 | rename_column :audits, :auditable_parent_type, :association_type 5 | end 6 | 7 | def self.down 8 | rename_column :audits, :association_type, :auditable_parent_type 9 | rename_column :audits, :association_id, :auditable_parent_id 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /doc/examples/tokens/read_only.xml.erb: -------------------------------------------------------------------------------- 1 | 6 | 7 | example.com 8 | <%= (Time.now + 2.hours) %> 9 | deny 10 | false 11 | false 12 | 13 | -------------------------------------------------------------------------------- /app/views/devise/confirmations/new.html.erb: -------------------------------------------------------------------------------- 1 |

Resend confirmation instructions

2 | 3 | <%= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |

<%= f.label :email %>
7 | <%= f.email_field :email %>

8 | 9 |

<%= f.submit "Resend confirmation instructions" %>

10 | <% end %> 11 | 12 | <%= render :partial => "devise/shared/links" %> -------------------------------------------------------------------------------- /spec/factories/auth_token_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | 3 | factory :auth_token do 4 | token '5zuld3g9dv76yosy' 5 | permissions({ 6 | 'policy' => 'deny', 7 | 'new' => false, 8 | 'remove' => false, 9 | 'protected' => [], 10 | 'protected_types' => [], 11 | 'allowed' => [ 12 | ['example.com', '*'], 13 | ['www.example.com', '*'] 14 | ] 15 | }) 16 | expires_at 3.hours.since 17 | end 18 | 19 | end 20 | -------------------------------------------------------------------------------- /spec/views/domains/apply_macro.html.haml_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "domains/apply_macro.html.haml" do 4 | before(:each) do 5 | @domain = FactoryGirl.create(:domain) 6 | @macro = FactoryGirl.create(:macro) 7 | 8 | assign(:domain, @domain) 9 | assign(:macros, Macro.all) 10 | 11 | render 12 | end 13 | 14 | it "should have a selection of macros" do 15 | rendered.should have_tag('select[name=macro_id]') 16 | end 17 | 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | 3 | protect_from_forgery 4 | 5 | # All pages require a login... 6 | before_filter :authenticate_user! 7 | 8 | # Stub 9 | def current_token 10 | # ... ? 11 | # p [ :current_token ] 12 | nil 13 | end 14 | helper_method :current_token 15 | 16 | # Stub 17 | def token_user? 18 | !!current_token 19 | end 20 | 21 | helper_method :token_user? 22 | end 23 | -------------------------------------------------------------------------------- /app/views/macro_steps/update.js.erb: -------------------------------------------------------------------------------- 1 | <% if @macro_step.errors.empty? %> 2 | $('#show_macro_step_<%= @macro_step.id %>').remove(); 3 | $('#edit_macro_step_<%= @macro_step.id %>').remove(); 4 | $('#marker_macro_step_<%= @macro_step.id %>').replaceWith('<%= escape_javascript render(:partial => @macro_step) %>'); 5 | humane("<%= escape_javascript t(:message_macro_step_updated) %>"); 6 | <% else %> 7 | humane("<%= escape_javascript t(:message_macro_step_cannot_update) %>"); 8 | <% end %> 9 | -------------------------------------------------------------------------------- /app/views/macros/index.html.haml: -------------------------------------------------------------------------------- 1 | %div{ :style => "display: none" } 2 | #new-macro= t :help_macro_new 3 | #delete-macro= t :help_macro_delete 4 | 5 | %ul.start 6 | %li 7 | = link_to new_macro_path do 8 | = info_icon('brick_add.png', 'new-macro') 9 | = link_to t(:label_macro_add), new_macro_path 10 | 11 | %h1.underline Macros 12 | 13 | - if @macros.size > 0 14 | %table 15 | = render :partial => @macros 16 | - else 17 | %em= t :message_macro_not_defined 18 | -------------------------------------------------------------------------------- /app/views/records/create.js.erb: -------------------------------------------------------------------------------- 1 | $('#record-form div.errorExplanation').remove(); 2 | 3 | <% if @record.errors.empty? %> 4 | $('#record-form-div').hide(); 5 | $('#record-form input:text').val(''); 6 | $('#record-table').append('<%= escape_javascript render(:partial => '/domains/record', :object => @record) %>'); 7 | humane('<%= t(:message_record_created) %>'); 8 | <% else %> 9 | $('#record-form').prepend('<%= escape_javascript error_messages_for(@record) %>'); 10 | <% end %> 11 | -------------------------------------------------------------------------------- /app/views/templates/_zone_template.html.haml: -------------------------------------------------------------------------------- 1 | %tr 2 | %td= link_to zone_template.name, zone_template_path( zone_template ) 3 | %td 4 | = link_to info_icon('table_edit.png', 'edit-template'), edit_zone_template_path( zone_template ) 5 | = link_to info_icon('table_delete.png', 'delete-template'), zone_template_path( zone_template ), :method => :delete, :confirm => t(:confirm_template_delete) 6 | - unless zone_template.has_soa? 7 | = info_icon('table_error.png', 'missing-soa') 8 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | PowerdnsOnRails::Application.config.session_store :cookie_store, :key => '_powerdns-on-rails_session' 4 | 5 | # Use the database for sessions instead of the cookie-based default, 6 | # which shouldn't be used to store highly confidential information 7 | # (create the session table with "rake db:sessions:create") 8 | # PowerdnsOnRails::Application.config.session_store :active_record_store 9 | -------------------------------------------------------------------------------- /config/cucumber.yml: -------------------------------------------------------------------------------- 1 | <% 2 | rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : "" 3 | rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}" 4 | std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} --strict --tags ~@wip" 5 | %> 6 | default: <%= std_opts %> features 7 | wip: --tags @wip:3 --wip features 8 | rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip 9 | -------------------------------------------------------------------------------- /app/views/macro_steps/create.js.erb: -------------------------------------------------------------------------------- 1 | $('#record-form div.errorExplanation').remove(); 2 | 3 | <% if !@macro_step.new_record? %> 4 | $('#record-form-div').hide(); 5 | $('#record-form input:text').val(''); 6 | $('#steps-table').append('<%= escape_javascript render(:partial => @macro_step) %>'); 7 | humane('<%= escape_javascript t(:message_macro_step_created) %>'); 8 | <% else %> 9 | $('#record-form').prepend('<%= escape_javascript error_messages_for(@macro_step) %>'); 10 | <% end %> 11 | -------------------------------------------------------------------------------- /spec/models/txt_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe TXT do 4 | context "when new" do 5 | 6 | it "should be invalid by default" do 7 | subject.should_not be_valid 8 | end 9 | 10 | it "should require content" do 11 | subject.should have(1).error_on(:content) 12 | end 13 | 14 | it "should not tamper with content" do 15 | subject.content = %Q{"google.com"} 16 | subject.content.should eql(%Q{"google.com"}) 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Someone has requested a link to change your password, and you can do this through the link below.

4 | 5 |

<%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %>

6 | 7 |

If you didn't request this, please ignore this email.

8 |

Your password won't change until you access the link above and create a new one.

9 | -------------------------------------------------------------------------------- /spec/views/templates/edit.html.haml_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "templates/edit.html.haml" do 4 | 5 | context "and existing templates" do 6 | 7 | before(:each) do 8 | @zone_template = FactoryGirl.create(:zone_template) 9 | assign(:zone_template, @zone_template) 10 | end 11 | 12 | it "should show the correct title" do 13 | render 14 | 15 | rendered.should have_tag('h1.underline', :content => 'Update Zone Template') 16 | end 17 | end 18 | 19 | end 20 | -------------------------------------------------------------------------------- /public/dispatch.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby18 2 | 3 | require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT) 4 | 5 | # If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like: 6 | # "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired 7 | require "dispatcher" 8 | 9 | ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun) 10 | Dispatcher.dispatch -------------------------------------------------------------------------------- /public/dispatch.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby18 2 | 3 | require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT) 4 | 5 | # If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like: 6 | # "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired 7 | require "dispatcher" 8 | 9 | ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun) 10 | Dispatcher.dispatch -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV["RAILS_ENV"] = "test" 2 | require File.expand_path('../../config/environment', __FILE__) 3 | require 'rails/test_help' 4 | 5 | class ActiveSupport::TestCase 6 | # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order. 7 | # 8 | # Note: You'll currently still have to declare fixtures explicitly in integration tests 9 | # -- they do not yet inherit this setting 10 | fixtures :all 11 | 12 | # Add more helper methods to be used by all tests here... 13 | end 14 | -------------------------------------------------------------------------------- /app/views/domains/apply_macro.html.haml: -------------------------------------------------------------------------------- 1 | - @page_title = t :title_macro_for, :domain => @domain.name 2 | 3 | %h1.underline= t :title_macro_apply 4 | 5 | %p= t :text_macro_description 6 | 7 | = form_tag( apply_macro_domain_path( @domain ) ) do 8 | %table.grid 9 | %tr 10 | %td= t :label_macro_sign 11 | %td= select_tag :macro_id, options_for_select( @macros.map { |m| [ m.name, m.id ] } ) 12 | %tr 13 | %td.right= link_to t(:generic_cancel), domain_path( @domain ) 14 | %td= submit_tag t(:label_macro_apply) 15 | -------------------------------------------------------------------------------- /app/views/records/update.js.erb: -------------------------------------------------------------------------------- 1 | <% if resource.errors.empty? %> 2 | $('#show_<%= resource.type.downcase %>_<%= resource.id %>').remove(); 3 | $('#edit_<%= resource.type.downcase %>_<%= resource.id %>').remove(); 4 | $('#marker_<%= resource.type.downcase %>_<%= resource.id %>').replaceWith('<%= escape_javascript render(:partial => '/domains/record', :object => resource) %>'); 5 | humane("<%= escape_javascript t(:message_record_updated) %>"); 6 | <% else %> 7 | humane("<%= escape_javascript t(:message_record_update_fail) %>"); 8 | <% end %> 9 | 10 | -------------------------------------------------------------------------------- /lib/record_patterns.rb: -------------------------------------------------------------------------------- 1 | require 'ipaddr' 2 | 3 | module RecordPatterns 4 | 5 | def hostname?( value ) 6 | value =~ /\A\S+\Z/ 7 | end 8 | 9 | def ip?( value ) 10 | ipv4?( value ) || ipv6?( value ) 11 | end 12 | 13 | def ipv4?( value ) 14 | value =~ /\A(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\z/ 15 | end 16 | 17 | def ipv6?( value ) 18 | begin 19 | IPAddr.new( "[#{value}]" ).ipv6? 20 | rescue ArgumentError 21 | return false 22 | end 23 | end 24 | 25 | end 26 | -------------------------------------------------------------------------------- /app/views/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 |

Sign in

2 | 3 | <%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %> 4 |

<%= f.label :email %>
5 | <%= f.email_field :email %>

6 | 7 |

<%= f.label :password %>
8 | <%= f.password_field :password %>

9 | 10 | <% if devise_mapping.rememberable? -%> 11 |

<%= f.check_box :remember_me %> <%= f.label :remember_me %>

12 | <% end -%> 13 | 14 |

<%= f.submit "Sign in" %>

15 | <% end %> 16 | 17 | <%= render :partial => "devise/shared/links" %> -------------------------------------------------------------------------------- /db/migrate/001_create_domains.rb: -------------------------------------------------------------------------------- 1 | class CreateDomains < ActiveRecord::Migration 2 | def self.up 3 | create_table :domains do |t| 4 | t.string :name 5 | t.string :master 6 | t.integer :last_check 7 | t.string :type, :default => 'NATIVE' 8 | t.integer :notified_serial 9 | t.string :account 10 | t.integer :ttl, :allow_null => false, :default => 86400 11 | 12 | t.timestamps 13 | end 14 | 15 | add_index :domains, :name 16 | end 17 | 18 | def self.down 19 | drop_table :domains 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/controllers/audits_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe AuditsController do 4 | 5 | before(:each) do 6 | sign_in(FactoryGirl.create(:admin)) 7 | end 8 | 9 | it "should have a search form" do 10 | get :index 11 | 12 | response.should render_template('audits/index') 13 | end 14 | 15 | it "should have a domain details page" do 16 | get :domain, :id => FactoryGirl.create(:domain).id 17 | 18 | assigns(:domain).should_not be_nil 19 | 20 | response.should render_template('audits/domain') 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/views/record_templates/update.js.erb: -------------------------------------------------------------------------------- 1 | <% if @record_template.errors.empty? %> 2 | $('#show_record_template_<%= @record_template.id %>').remove(); 3 | $('#edit_record_template_<%= @record_template.id %>').remove(); 4 | $('#marker_record_template_<%= @record_template.id %>').replaceWith('<%= escape_javascript render(:partial => '/templates/record_template', :object => @record_template) %>'); 5 | humane("<%= escape_javascript t(:message_record_template_updated) %>"); 6 | <% else %> 7 | humane("<%= escape_javascript t(:message_record_template_update_fail) %>"); 8 | <% end %> 9 | -------------------------------------------------------------------------------- /db/migrate/005_create_roles.rb: -------------------------------------------------------------------------------- 1 | class CreateRoles < ActiveRecord::Migration 2 | def self.up 3 | create_table "roles" do |t| 4 | t.column :name, :string 5 | end 6 | 7 | # generate the join table 8 | create_table "roles_users", :id => false do |t| 9 | t.column "role_id", :integer 10 | t.column "user_id", :integer 11 | end 12 | add_index "roles_users", "role_id" 13 | add_index "roles_users", "user_id" 14 | end 15 | 16 | def self.down 17 | drop_table "roles" 18 | drop_table "roles_users" 19 | end 20 | end -------------------------------------------------------------------------------- /app/models/loc.rb: -------------------------------------------------------------------------------- 1 | # See #LOC 2 | 3 | # = Name Server Record (LOC) 4 | # 5 | # In the Domain Name System, a LOC record (RFC 1876) is a means for expressing 6 | # geographic location information for a domain name. 7 | # It contains WGS84 Latitude, Longitude and Altitude information together with 8 | # host/subnet physical size and location accuracy. This information can be 9 | # queried by other computers connected to the Internet. 10 | # 11 | # Obtained from http://en.wikipedia.org/wiki/LOC_record 12 | # 13 | class LOC < Record 14 | 15 | validates_presence_of :content 16 | 17 | end 18 | -------------------------------------------------------------------------------- /app/models/mx.rb: -------------------------------------------------------------------------------- 1 | # See #MX 2 | 3 | # = Mail Exchange Record (MX) 4 | # Defined in RFC 1035. Specifies the name and relative preference of mail 5 | # servers (mail exchangers in the DNS jargon) for the zone. 6 | # 7 | # Obtained from http://www.zytrax.com/books/dns/ch8/mx.html 8 | # 9 | class MX < Record 10 | 11 | validates_numericality_of :prio, 12 | :greater_than_or_equal_to => 0, 13 | :less_than_or_equal_to => 65535, 14 | :only_integer => true 15 | 16 | validates :content, :presence => true, :hostname => true 17 | 18 | def supports_prio? 19 | true 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/helpers/application_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ApplicationHelper do 4 | describe "link_to_cancel" do 5 | it "on new records should link to index" do 6 | html = helper.link_to_cancel( Macro.new ) 7 | html.should have_tag('a[href="/macros"]', :content => 'Cancel') 8 | end 9 | 10 | it "on existing records should link to show" do 11 | macro = FactoryGirl.create(:macro) 12 | html = helper.link_to_cancel( macro ) 13 | html.should have_tag("a[href='/macros/#{macro.id}']", :content => 'Cancel') 14 | end 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /app/views/devise/registrations/new.html.erb: -------------------------------------------------------------------------------- 1 |

Sign up

2 | 3 | <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |

<%= f.label :email %>
7 | <%= f.email_field :email %>

8 | 9 |

<%= f.label :password %>
10 | <%= f.password_field :password %>

11 | 12 |

<%= f.label :password_confirmation %>
13 | <%= f.password_field :password_confirmation %>

14 | 15 |

<%= f.submit "Sign up" %>

16 | <% end %> 17 | 18 | <%= render :partial => "devise/shared/links" %> 19 | -------------------------------------------------------------------------------- /app/views/domains/index.html.haml: -------------------------------------------------------------------------------- 1 | %div{ :style => "display: none" } 2 | #new-zone= t :help_adds_new_domain 3 | #delete-zone= t :help_delete_domain 4 | #zone-edit=t :help_zone_edit 5 | #delete-zone=t :help_delete_zone 6 | #audits=t :help_audits 7 | #macro=t :help_macro 8 | 9 | %ul.start 10 | %li 11 | = link_to info_icon('database_add.png', 'new-zone'), new_domain_path 12 | = link_to t(:label_add_new_zone), new_domain_path 13 | 14 | %h1.underline= t :title_domains_list 15 | 16 | %table 17 | = render :partial => 'domain', :collection => @domains 18 | 19 | = will_paginate @domains 20 | -------------------------------------------------------------------------------- /app/views/templates/index.html.haml: -------------------------------------------------------------------------------- 1 | %h1.underline Zone Templates 2 | 3 | %div{ :style => "display: none;" } 4 | #new-template= t :help_template_zone_new 5 | #edit-template= t :help_template_zone_edit 6 | #delete-template= t :help_template_zone_delete 7 | #missing-soa= t :help_template_zone_soa_missing 8 | 9 | %table.grid 10 | %tr 11 | %td 12 | = link_to info_icon('table_add.png', 'new-template'), new_zone_template_path 13 | = link_to t(:label_template_create_new), new_zone_template_path 14 | %td   15 | = render :partial => 'zone_template', :collection => @zone_templates 16 | -------------------------------------------------------------------------------- /app/models/txt.rb: -------------------------------------------------------------------------------- 1 | # See #TXT 2 | 3 | # = Text Record (TXT) 4 | # Provides the ability to associate some text with a host or other name. The TXT 5 | # record is used to define the Sender Policy Framework (SPF) information record 6 | # which may be used to validate legitimate email sources from a domain. The SPF 7 | # record while being increasing deployed is not (July 2004) a formal IETF RFC 8 | # standard. 9 | # 10 | # Obtained from http://www.zytrax.com/books/dns/ch8/txt.html 11 | class TXT < Record 12 | 13 | validates :content, format: { with: /\A".*"\Z/, message: "TXT records must be in quotes" } 14 | 15 | end 16 | -------------------------------------------------------------------------------- /app/models/a.rb: -------------------------------------------------------------------------------- 1 | # See #A 2 | 3 | # = IPv4 Address Record (A) 4 | # 5 | # Defined in RFC 1035. Forward maps a host name to IPv4 address. The only 6 | # parameter is an IP address in dotted decimal format. The IP address in not 7 | # terminated with a '.' (dot). Valid host name format (a.k.a 'label' in DNS 8 | # jargon). If host name is BLANK (or space) then the last valid name (or label) 9 | # is substituted. 10 | # 11 | # Obtained from http://www.zytrax.com/books/dns/ch8/a.html 12 | # 13 | class A < Record 14 | 15 | # Only accept valid IPv4 addresses 16 | validates :content, :presence => true, :ip_address => true 17 | 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/templates_controller.rb: -------------------------------------------------------------------------------- 1 | class TemplatesController < InheritedResources::Base 2 | 3 | defaults :resource_class => ZoneTemplate, :collection_name => 'zone_templates', :instance_name => 'zone_template' 4 | respond_to :html, :xml, :json 5 | 6 | protected 7 | 8 | def collection 9 | @zone_templates = ZoneTemplate.user(current_user).all 10 | end 11 | 12 | public 13 | 14 | def create 15 | @zone_template = ZoneTemplate.new(params[:zone_template]) 16 | @zone_template.user = current_user unless current_user.admin? 17 | 18 | create! { zone_template_path( @zone_template ) } 19 | end 20 | 21 | end 22 | -------------------------------------------------------------------------------- /spec/views/macros/index.html.haml_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "macros/index.html.haml" do 4 | 5 | it "should render a list of macros" do 6 | 2.times { |i| FactoryGirl.create(:macro, :name => "Macro #{i}") } 7 | assign(:macros, Macro.all) 8 | 9 | render 10 | 11 | rendered.should have_tag('h1', :content => 'Macros') 12 | render.should have_tag("table a[href^='/macro']") 13 | end 14 | 15 | it "should indicate no macros are present" do 16 | assign(:macros, Macro.all) 17 | 18 | render 19 | 20 | rendered.should have_tag('em', :content => "don't have any macros") 21 | end 22 | 23 | end 24 | -------------------------------------------------------------------------------- /app/views/devise/passwords/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Change your password

2 | 3 | <%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f| %> 4 | <%= devise_error_messages! %> 5 | <%= f.hidden_field :reset_password_token %> 6 | 7 |

<%= f.label :password, "New password" %>
8 | <%= f.password_field :password %>

9 | 10 |

<%= f.label :password_confirmation, "Confirm new password" %>
11 | <%= f.password_field :password_confirmation %>

12 | 13 |

<%= f.submit "Change my password" %>

14 | <% end %> 15 | 16 | <%= render :partial => "devise/shared/links" %> -------------------------------------------------------------------------------- /spec/factories/macro_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | 3 | factory :macro do 4 | name 'Move to West Coast' 5 | active true 6 | end 7 | 8 | factory :macro_step_create, :class => MacroStep do 9 | action 'create' 10 | record_type 'A' 11 | name 'auto' 12 | content '127.0.0.1' 13 | end 14 | 15 | factory :macro_step_change, :class => MacroStep do 16 | action 'update' 17 | record_type 'A' 18 | name 'www' 19 | content '127.1.1.1' 20 | end 21 | 22 | factory :macro_step_remove, :class => MacroStep do 23 | action 'remove' 24 | record_type 'A' 25 | name 'ftp' 26 | end 27 | 28 | end 29 | 30 | -------------------------------------------------------------------------------- /app/views/record_templates/create.js.erb: -------------------------------------------------------------------------------- 1 | $('#record-form div.errorExplanation').remove(); 2 | 3 | <% if !@record_template.new_record? %> 4 | $('#record-form-div').hide(); 5 | $('#record-form input:text').val(''); 6 | $('#record-table').append('<%= escape_javascript render(:partial => '/templates/record_template', :object => @record_template) %>'); 7 | humane('<%= escape_javascript t(:message_record_template_created) %>'); 8 | 9 | <% if @record_template.record_type == 'SOA' %> 10 | $('#soa-warning').hide(); 11 | <% end %> 12 | <% else %> 13 | $('#record-form').prepend('<%= escape_javascript error_messages_for(@record_template) %>'); 14 | <% end %> 15 | -------------------------------------------------------------------------------- /app/models/spf.rb: -------------------------------------------------------------------------------- 1 | # See #SPF 2 | 3 | # = Text Record (SPF) 4 | # 5 | # In computing, Sender Policy Framework (SPF) allows software to identify 6 | # messages that are or are not authorized to use the domain name in the SMTP 7 | # HELO and MAIL FROM (Return-Path) commands, based on information published in a 8 | # sender policy of the domain owner. Forged return paths are common in e-mail 9 | # spam and result in backscatter. SPF is defined in RFC 4408 10 | # 11 | # Obtained from http://en.wikipedia.org/wiki/Sender_Policy_Framework 12 | # 13 | class SPF < Record 14 | 15 | validates :content, format: { with: /\A".*"\Z/, message: "SPF records must be in quotes" } 16 | 17 | end 18 | -------------------------------------------------------------------------------- /app/models/aaaa.rb: -------------------------------------------------------------------------------- 1 | # See #AAAA 2 | 3 | # = IPv6 Address Record (AAAA) 4 | # 5 | # The current IETF recommendation is to use AAAA (Quad A) RR for forward mapping 6 | # and PTR RRs for reverse mapping when defining IPv6 networks. The IPv6 AAAA RR 7 | # is defined in RFC 3596. RFC 3363 changed the status of the A6 RR (defined in 8 | # RFC 2874 from a PROPOSED STANDARD to EXPERIMENTAL due primarily to performance 9 | # and operational concerns. 10 | # 11 | # Obtained from http://www.zytrax.com/books/dns/ch8/aaaa.html 12 | # 13 | class AAAA < Record 14 | 15 | # Only accept valid IPv6 addresses 16 | validates :content, :presence => true, :ip_address => { :ipv6 => true } 17 | 18 | end 19 | -------------------------------------------------------------------------------- /db/migrate/002_create_records.rb: -------------------------------------------------------------------------------- 1 | class CreateRecords < ActiveRecord::Migration 2 | def self.up 3 | create_table :records do |t| 4 | t.integer :domain_id, :null => false 5 | t.string :name, :null => false 6 | t.string :type, :null => false 7 | t.string :content, :null => false 8 | t.integer :ttl, :null => false 9 | t.integer :prio 10 | t.integer :change_date, :null => true 11 | 12 | t.timestamps :null => true 13 | end 14 | 15 | add_index :records, :domain_id 16 | add_index :records, :name 17 | add_index :records, [ :name, :type ] 18 | end 19 | 20 | def self.down 21 | drop_table :records 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/models/user_mailer.rb: -------------------------------------------------------------------------------- 1 | class UserMailer < ActionMailer::Base 2 | def signup_notification(user) 3 | setup_email(user) 4 | @subject += I18n.t(:message_user_activate_account) 5 | 6 | @body[:url] = "http://YOURSITE/activate/#{user.activation_code}" 7 | 8 | end 9 | 10 | def activation(user) 11 | setup_email(user) 12 | @subject += I18n.t(:message_user_activated) 13 | @body[:url] = "http://YOURSITE/" 14 | end 15 | 16 | protected 17 | def setup_email(user) 18 | @recipients = "#{user.email}" 19 | @from = "ADMINEMAIL" 20 | @subject = "[YOURSITE] " 21 | @sent_on = Time.now 22 | @body[:user] = user 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/controllers/reports_controller.rb: -------------------------------------------------------------------------------- 1 | class ReportsController < ApplicationController 2 | 3 | before_filter do 4 | unless current_user.admin? 5 | redirect_to root_url 6 | end 7 | end 8 | 9 | # search for a specific user 10 | def index 11 | @users = User.where(:admin => false).paginate(:page => params[:page]) 12 | @total_domains = Domain.count 13 | @system_domains = Domain.where('user_id IS NULL').count 14 | end 15 | 16 | def results 17 | if params[:q].chomp.blank? 18 | redirect_to reports_path 19 | else 20 | @results = User.search(params[:q], params[:page]) 21 | end 22 | end 23 | 24 | def view 25 | @user = User.find(params[:id]) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < InheritedResources::Base 2 | 3 | before_filter do 4 | unless current_user.admin? 5 | redirect_to root_url 6 | end 7 | end 8 | 9 | def update 10 | # strip out blank params 11 | params[:user].delete_if { |k,v| v.blank? } 12 | update! 13 | end 14 | 15 | def suspend 16 | resource.suspend! 17 | redirect_to users_path 18 | end 19 | 20 | def unsuspend 21 | resource.unsuspend! 22 | redirect_to users_path 23 | end 24 | 25 | def destroy 26 | resource.delete! 27 | redirect_to users_path 28 | end 29 | 30 | def purge 31 | resource.destroy 32 | redirect_to users_path 33 | end 34 | 35 | end 36 | -------------------------------------------------------------------------------- /app/models/srv.rb: -------------------------------------------------------------------------------- 1 | # See #SRV 2 | 3 | # = Service Record (SRV) 4 | # 5 | # An SRV record or Service record is a category of data in the Internet Domain 6 | # Name System specifying information on available services. It is defined in 7 | # RFC 2782. Newer internet protocols such as SIP and XMPP often require SRV 8 | # support from clients. 9 | # 10 | # Obtained from http://en.wikipedia.org/wiki/SRV_record 11 | # 12 | # See also http://www.zytrax.com/books/dns/ch8/srv.html 13 | # 14 | class SRV < Record 15 | 16 | validates_numericality_of :prio, 17 | :greater_than_or_equal_to => 0 18 | 19 | validates_presence_of :content 20 | 21 | # We support priorities 22 | def supports_prio? 23 | true 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /db/migrate/003_create_templates.rb: -------------------------------------------------------------------------------- 1 | class CreateTemplates < ActiveRecord::Migration 2 | def self.up 3 | create_table :zone_templates do |t| 4 | t.string :name 5 | t.integer :ttl, :allow_null => false, :default => 86400 6 | 7 | t.timestamps 8 | end 9 | 10 | create_table :record_templates do |t| 11 | t.integer :zone_template_id 12 | t.string :name 13 | t.string :record_type, :null => false 14 | t.string :content, :null => false 15 | t.integer :ttl, :null => false 16 | t.integer :prio 17 | 18 | t.timestamps 19 | end 20 | end 21 | 22 | def self.down 23 | drop_table :zone_templates 24 | drop_table :record_templates 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /db/migrate/20081228121040_create_macros.rb: -------------------------------------------------------------------------------- 1 | class CreateMacros < ActiveRecord::Migration 2 | def self.up 3 | create_table :macros do |t| 4 | t.string :name, :description 5 | t.references :user 6 | t.boolean :active, :default => false 7 | 8 | t.timestamps 9 | end 10 | 11 | create_table :macro_steps do |t| 12 | t.references :macro 13 | t.string :action, :record_type, :name, :content 14 | t.integer :ttl, :prio, :position 15 | t.boolean :active, :default => true 16 | t.string :note 17 | 18 | t.timestamps 19 | end 20 | 21 | end 22 | 23 | def self.down 24 | drop_table :macros 25 | drop_table :macro_steps 26 | end 27 | end 28 | 29 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | 3 | bundler_args: --without development 4 | 5 | rvm: 6 | - 2.2 7 | - 2.1 8 | - 2.0 9 | - rbx-19mode 10 | - 1.9.3 11 | - 1.9.2 12 | 13 | env: 14 | - DB=sqlite 15 | - DB=mysql 16 | - DB=postgresql 17 | 18 | before_script: 19 | - mv config/database.yml.template config/database.yml 20 | - rake generate_secret_token 21 | - mysql -e 'create database powerdns_test;' 22 | - psql -c 'create database powerdns_test;' -U postgres 23 | - bundle exec rake db:migrate 24 | - bundle exec rake db:seed 25 | 26 | script: "bundle exec rake spec" 27 | 28 | matrix: 29 | allow_failures: 30 | - rvm: 2.2 31 | - rvm: jruby-19mode 32 | - rvm: rbx-19mode 33 | - env: DB=sqlite 34 | 35 | sudo: false 36 | -------------------------------------------------------------------------------- /app/controllers/search_controller.rb: -------------------------------------------------------------------------------- 1 | class SearchController < ApplicationController 2 | 3 | def results 4 | if params[:q].chomp.blank? 5 | respond_to do |format| 6 | format.html { redirect_to root_path } 7 | format.json { render :status => 404, :json => { :error => "Missing 'q' parameter" } } 8 | end 9 | else 10 | @results = Domain.search(params[:q], params[:page], current_user) 11 | 12 | respond_to do |format| 13 | format.html do 14 | if @results.size == 1 15 | redirect_to domain_path(@results.pop) 16 | end 17 | end 18 | format.json do 19 | render :json => @results.to_json(:only => [:id, :name]) 20 | end 21 | end 22 | end 23 | end 24 | 25 | end 26 | -------------------------------------------------------------------------------- /app/views/domains/_domain.html.haml: -------------------------------------------------------------------------------- 1 | %tr 2 | %td 3 | = link_to domain.name, domain_path( domain ) 4 | %td   5 | %td 6 | = link_to info_icon('table_edit.png', 'zone-edit'), edit_domain_path(domain) 7 | = link_to info_icon('report_magnify.png', 'audits'), audits_path(:action => :domain, :id => domain) 8 | - unless domain.slave? 9 | = link_to info_icon('brick_go.png', 'macro'), apply_macro_domain_path( domain ) 10 | - if current_user.admin? && domain.user 11 | = link_to_function info_icon('database_delete.png', 'delete-zone'), "deleteDomain();" , :id => "delete-link-buffer" 12 | - elsif !current_token 13 | = link_to info_icon('database_delete.png', 'delete-zone'), domain_path( domain ), :method => :delete, :confirm => t(:confirm_domain_delete, :domain => domain.name) 14 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The change you wanted was rejected.

23 |

Maybe you tried to change something you didn't have access to.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The page you were looking for doesn't exist.

23 |

You may have mistyped the address or the page may have moved.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

We're sorry, but something went wrong.

23 |

We've been notified about this issue and we'll take a look at it shortly.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/tasks/secret_token.rake: -------------------------------------------------------------------------------- 1 | file 'config/initializers/secret_token.rb' do 2 | path = Rails.root + 'config' + 'initializers' + 'secret_token.rb' 3 | secret = SecureRandom.hex(64) 4 | path.open('w') do |io| 5 | io.write <<-STRING 6 | # This file was generated by 'rake generate_secret_token', and should 7 | # not be made visible to public. 8 | # 9 | # Your secret key for verifying cookie session data integrity. If you 10 | # change this key, all old sessions will become invalid! Make sure the 11 | # secret is at least 30 characters and all random, no regular words or 12 | # you'll be exposed to dictionary attacks. 13 | PowerdnsOnRails::Application.config.secret_token = '#{secret}' 14 | STRING 15 | end 16 | end 17 | 18 | desc 'Generates a secret token for the application.' 19 | task :generate_secret_token => ['config/initializers/secret_token.rb'] -------------------------------------------------------------------------------- /spec/models/aaaa_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe AAAA do 4 | 5 | context "when new" do 6 | 7 | it "should be invalid by default" do 8 | subject.should_not be_valid 9 | end 10 | 11 | it "should only accept IPv6 address as content" 12 | 13 | it "should not act as a CNAME" do 14 | subject.content = 'google.com' 15 | subject.should have(1).error_on(:content) 16 | end 17 | 18 | it "should accept a valid ipv6 address" do 19 | subject.content = "2001:0db8:85a3:0000:0000:8a2e:0370:7334" 20 | subject.should have(:no).error_on(:content) 21 | end 22 | 23 | it "should not accept new lines in content" do 24 | subject.content = "2001:0db8:85a3:0000:0000:8a2e:0370:7334\nHELLO WORLD" 25 | subject.should have(1).error_on(:content) 26 | end 27 | 28 | end 29 | 30 | end 31 | -------------------------------------------------------------------------------- /config/initializers/new_rails_defaults.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # These settings change the behavior of Rails 2 apps and will be defaults 4 | # for Rails 3. You can remove this initializer when Rails 3 is released. 5 | 6 | if defined?(ActiveRecord) 7 | # Include Active Record class name as root for JSON serialized output. 8 | ActiveRecord::Base.include_root_in_json = true 9 | 10 | # Store the full class name (including module namespace) in STI type column. 11 | ActiveRecord::Base.store_full_sti_class = true 12 | end 13 | 14 | # Use ISO 8601 format for JSON serialized times and dates. 15 | ActiveSupport.use_standard_json_time_format = true 16 | 17 | # Don't escape HTML entities in JSON, leave that for the #json_escape helper. 18 | # if you're including raw json in an HTML page. 19 | ActiveSupport.escape_html_entities_in_json = false -------------------------------------------------------------------------------- /lib/tasks/tokens.rake: -------------------------------------------------------------------------------- 1 | require 'erb' 2 | 3 | desc "Generate example authentication tokens" 4 | task :tokens => :environment do 5 | puts "" 6 | puts "Generating tokens" 7 | puts 8 | 9 | # Parse each token 10 | Dir["doc/examples/tokens/*.erb"].each do |erb| 11 | xml_file = File.basename(erb, '.erb') 12 | File.open("tmp/token_#{xml_file}", "w+") do |f| 13 | f.write( ERB.new( File.read( erb ) ).result( binding ) ) 14 | end 15 | 16 | puts "* Generated token: tmp/token_#{xml_file}" 17 | end 18 | 19 | puts <<-EOF 20 | 21 | To use the example tokens, please make sure you have seeded the database by 22 | running 'rake db:seed'. 23 | 24 | To execute a token, you can use the following curl command: 25 | 26 | curl -X POST --basic -u token:secret -d @tmp/token_read_only.xml -H "Content-type: text/xml" http://localhost:3000/auth_token.xml 27 | 28 | EOF 29 | end 30 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 5 | VAGRANTFILE_API_VERSION = "2" 6 | 7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 8 | # Every Vagrant virtual environment requires a box to build off of. 9 | config.vm.box = "ubuntu/trusty64" 10 | 11 | # Create a forwarded port mapping which allows access to a specific port 12 | # within the machine from a port on the host machine. In the example below, 13 | # accessing "localhost:8080" will access port 80 on the guest machine. 14 | config.vm.network "forwarded_port", guest: 3000, host: 8080 15 | 16 | # If true, then any SSH connections made will enable agent forwarding. 17 | # Default value: false 18 | # config.ssh.forward_agent = true 19 | 20 | config.vm.provision "shell", path: "script/provision", privileged: false 21 | end 22 | -------------------------------------------------------------------------------- /spec/views/macros/show.html.haml_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "macros/show.html.haml" do 4 | before(:each) do 5 | @macro = FactoryGirl.create(:macro) 6 | FactoryGirl.create(:macro_step_create, :macro => @macro) 7 | 8 | assign(:macro, @macro) 9 | 10 | render 11 | end 12 | 13 | it "should have the name of the macro" do 14 | rendered.should have_tag('h1', :content => @macro.name) 15 | end 16 | 17 | it "should have an overview table" do 18 | rendered.should have_tag('table.grid td', :content => "Name") 19 | rendered.should have_tag('table.grid td', :content => "Description") 20 | rendered.should have_tag('table.grid td', :content => "Active") 21 | end 22 | 23 | it "should have a list of steps" do 24 | rendered.should have_tag('h1', :content => 'Macro Steps') 25 | rendered.should have_tag('table#steps-table td', :content => "1") 26 | end 27 | 28 | end 29 | -------------------------------------------------------------------------------- /db/migrate/004_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration 2 | def self.up 3 | create_table "users", :force => true do |t| 4 | t.column :login, :string 5 | t.column :email, :string 6 | t.column :crypted_password, :string, :limit => 40 7 | t.column :salt, :string, :limit => 40 8 | t.column :created_at, :datetime 9 | t.column :updated_at, :datetime 10 | t.column :remember_token, :string 11 | t.column :remember_token_expires_at, :datetime 12 | t.column :activation_code, :string, :limit => 40 13 | t.column :activated_at, :datetime 14 | t.column :state, :string, :null => :no, :default => 'passive' 15 | t.column :deleted_at, :datetime 16 | end 17 | end 18 | 19 | def self.down 20 | drop_table "users" 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/controllers/macros_controller.rb: -------------------------------------------------------------------------------- 1 | class MacrosController < InheritedResources::Base 2 | 3 | protected 4 | 5 | def resource 6 | @macro = Macro.user( current_user ).find(params[:id]) 7 | end 8 | 9 | def collection 10 | @macros = Macro.user(current_user) 11 | end 12 | 13 | public 14 | 15 | def new 16 | new! do |format| 17 | format.html { render :action => :edit } 18 | end 19 | end 20 | 21 | def create 22 | create! do |success, failure| 23 | failure.html { render :action => :edit } 24 | end 25 | end 26 | 27 | def create_resource(macro) 28 | macro.user = macro_owner_from_params 29 | super 30 | end 31 | 32 | def update_resource(macro, attributes) 33 | super 34 | macro.user = macro_owner_from_params 35 | end 36 | 37 | protected 38 | def macro_owner_from_params 39 | current_user.admin? ? User.where(id: params[:macro][:user_id]).first : current_user 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /app/controllers/record_templates_controller.rb: -------------------------------------------------------------------------------- 1 | class RecordTemplatesController < ApplicationController 2 | 3 | def create 4 | @record_template = RecordTemplate.new(params[:record_template]) 5 | @zone_template = ZoneTemplate.find(params[:zone_template][:id]) 6 | @record_template.zone_template = @zone_template 7 | @record_template.save 8 | 9 | respond_to do |format| 10 | format.js 11 | end 12 | end 13 | 14 | def update 15 | @record_template = RecordTemplate.find(params[:id]) 16 | 17 | @record_template.update_attributes(params[:record_template]) 18 | 19 | respond_to do |format| 20 | format.js 21 | end 22 | end 23 | 24 | def destroy 25 | @record_template = RecordTemplate.find(params[:id]) 26 | zt = @record_template.zone_template 27 | @record_template.destroy 28 | 29 | flash[:info] = t(:message_record_template_removed) 30 | redirect_to zone_template_path( zt ) 31 | end 32 | 33 | end 34 | -------------------------------------------------------------------------------- /public/dispatch.fcgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby18 2 | # 3 | # You may specify the path to the FastCGI crash log (a log of unhandled 4 | # exceptions which forced the FastCGI instance to exit, great for debugging) 5 | # and the number of requests to process before running garbage collection. 6 | # 7 | # By default, the FastCGI crash log is RAILS_ROOT/log/fastcgi.crash.log 8 | # and the GC period is nil (turned off). A reasonable number of requests 9 | # could range from 10-100 depending on the memory footprint of your app. 10 | # 11 | # Example: 12 | # # Default log path, normal GC behavior. 13 | # RailsFCGIHandler.process! 14 | # 15 | # # Default log path, 50 requests between GC. 16 | # RailsFCGIHandler.process! nil, 50 17 | # 18 | # # Custom log path, normal GC behavior. 19 | # RailsFCGIHandler.process! '/var/log/myapp_fcgi_crash.log' 20 | # 21 | require File.dirname(__FILE__) + "/../config/environment" 22 | require 'fcgi_handler' 23 | 24 | RailsFCGIHandler.process! 25 | -------------------------------------------------------------------------------- /spec/views/domains/new.html.haml_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "domains/new.html.haml" do 4 | 5 | before(:each) do 6 | assign(:domain, Domain.new) 7 | 8 | view.stubs(:current_user).returns( FactoryGirl.create(:admin) ) 9 | end 10 | 11 | it "should have a link to create a zone template if no zone templates are present" do 12 | assign(:zone_templates, []) 13 | 14 | render 15 | 16 | rendered.should have_selector("a[href='#{new_zone_template_path}']") 17 | rendered.should_not have_selector("select[name*=zone_template_id]") 18 | end 19 | 20 | it "should have a list of zone templates to select from" do 21 | zt = FactoryGirl.create(:zone_template) 22 | FactoryGirl.create(:template_soa, :zone_template => zt) 23 | 24 | render 25 | 26 | rendered.should have_selector("select[name*=zone_template_id]") 27 | rendered.should_not have_selector("a[href='#{new_zone_template_path}']") 28 | end 29 | 30 | end 31 | -------------------------------------------------------------------------------- /CREDITS.textile: -------------------------------------------------------------------------------- 1 | h1. Credits 2 | 3 | "Giving credit where credit is due is a very rewarding habit to form. Its rewards are inestimable." -- Loretta Young 4 | 5 | h2. Icons 6 | 7 | Silk icon set 1.3 by Mark James (http://www.famfamfam.com/lab/icons/silk/) 8 | 9 | h2. Tooltips 10 | 11 | Shamelessly copied from Nick Stakenburg's Prototip 2 library. Since this project is licensed under the MIT License, it should not conflict with the Creative Commons Attribution-Noncommercial-No Derivative Works license. You can find Prototip 2 at http://www.nickstakenburg.com/projects/prototip2/ 12 | 13 | h2. Ruby and Ruby on Rails 14 | 15 | This list is too big, but everyone involved in making Ruby, and Ruby on Rails, thanks a million for changing my life. 16 | 17 | h2. ISP in a Box 18 | 19 | My partners at ISP in a Box, thanks for the time, resources, motivation and endurance it has taken to place this project in the public domain. Hoping this will grow to exceed all your expectations. 20 | -------------------------------------------------------------------------------- /features/support/paths.rb: -------------------------------------------------------------------------------- 1 | module NavigationHelpers 2 | # Maps a name to a path. Used by the 3 | # 4 | # When /^I go to (.+)$/ do |page_name| 5 | # 6 | # step definition in web_steps.rb 7 | # 8 | def path_to(page_name) 9 | case page_name 10 | 11 | when /^the home\s?page$/ 12 | '/' 13 | 14 | # Add more mappings here. 15 | # Here is an example that pulls values out of the Regexp: 16 | # 17 | # when /^(.*)'s profile page$/i 18 | # user_profile_path(User.find_by_login($1)) 19 | 20 | else 21 | begin 22 | page_name =~ /^the (.*) page$/ 23 | path_components = $1.split(/\s+/) 24 | self.send(path_components.push('path').join('_').to_sym) 25 | rescue NoMethodError, ArgumentError 26 | raise "Can't find mapping from \"#{page_name}\" to a path.\n" + 27 | "Now, go and add a mapping in #{__FILE__}" 28 | end 29 | end 30 | end 31 | end 32 | 33 | World(NavigationHelpers) 34 | -------------------------------------------------------------------------------- /app/views/audits/domain.html.haml: -------------------------------------------------------------------------------- 1 | %h1.underline 2 | = t :title_audit_information_for 3 | = link_to @domain.name, domain_path(@domain) 4 | 5 | %p= t :text_audit_click_on_revision_for_detail 6 | 7 | %h2.underline= t :title_audit_domain_revisions 8 | 9 | - if @domain.audits.size > 0 10 | %ul 11 | = render :partial => 'domain_audit', :collection => sort_audits_by_date( @domain.audits ) 12 | - else 13 | %p 14 | %em= t :label_audit_no_domain_revisions 15 | 16 | %h2.underline= t :title_audit_rr_revisions 17 | 18 | - if @domain.associated_audits.size > 0 19 | %p= t :text_audit_order_description 20 | 21 | %ul 22 | = render :partial => 'record_audit', :collection => sort_audits_by_date( @domain.associated_audits ) 23 | - else 24 | %p 25 | %em= t :text_audit_no_resource_revision 26 | 27 | :javascript 28 | function toggleDomainAudit(id){ 29 | $('audit_' + id).toggle(); 30 | } 31 | function toggleRecordAudit(id){ 32 | $('audit_' + id).toggle(); 33 | } 34 | -------------------------------------------------------------------------------- /spec/views/templates/new.html.haml_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "templates/new.html.haml" do 4 | 5 | context "and new templates" do 6 | before(:each) do 7 | assign(:zone_template, ZoneTemplate.new) 8 | end 9 | 10 | it "should have a list of users if provided" do 11 | FactoryGirl.create(:quentin) 12 | 13 | render 14 | 15 | rendered.should have_tag('select#zone_template_user_id') 16 | end 17 | 18 | it "should render without a list of users" do 19 | render 20 | 21 | rendered.should_not have_tag('select#zone_template_user_id') 22 | end 23 | 24 | it "should render with a missing list of users (nil)" do 25 | render 26 | 27 | rendered.should_not have_tag('select#zone_template_user_id') 28 | end 29 | 30 | it "should show the correct title" do 31 | render 32 | 33 | rendered.should have_tag('h1.underline', :content => 'New Zone Template') 34 | end 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /db/migrate/20120819220815_rename_association_to_associated.rb: -------------------------------------------------------------------------------- 1 | class RenameAssociationToAssociated < ActiveRecord::Migration 2 | def self.up 3 | if index_exists? :audits, [:association_id, :association_type], :name => 'association_index' 4 | remove_index :audits, :name => 'association_index' 5 | end 6 | 7 | rename_column :audits, :association_id, :associated_id 8 | rename_column :audits, :association_type, :associated_type 9 | 10 | add_index :audits, [:associated_id, :associated_type], :name => 'associated_index' 11 | end 12 | 13 | def self.down 14 | if index_exists? :audits, [:associated_id, :associated_type], :name => 'associated_index' 15 | remove_index :audits, :name => 'associated_index' 16 | end 17 | 18 | rename_column :audits, :associated_type, :association_type 19 | rename_column :audits, :associated_id, :association_id 20 | 21 | add_index :audits, [:association_id, :association_type], :name => 'association_index' 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /db/migrate/009_add_audit_support.rb: -------------------------------------------------------------------------------- 1 | class AddAuditSupport < ActiveRecord::Migration 2 | def self.up 3 | create_table :audits, :force => true do |t| 4 | t.column :auditable_id, :integer 5 | t.column :auditable_type, :string 6 | t.column :auditable_parent_id, :integer 7 | t.column :auditable_parent_type, :string 8 | t.column :user_id, :integer 9 | t.column :user_type, :string 10 | t.column :username, :string 11 | t.column :action, :string 12 | t.column :changes, :text 13 | t.column :version, :integer, :default => 0 14 | t.column :created_at, :datetime 15 | end 16 | 17 | add_index :audits, [:auditable_id, :auditable_type], :name => 'auditable_index' 18 | add_index :audits, [:auditable_parent_id, :auditable_parent_type], :name => 'auditable_parent_index' 19 | add_index :audits, [:user_id, :user_type], :name => 'user_index' 20 | add_index :audits, :created_at 21 | end 22 | 23 | def self.down 24 | drop_table :audits 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /app/views/devise/registrations/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Edit <%= resource_name.to_s.humanize %>

2 | 3 | <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |

<%= f.label :email %>
7 | <%= f.email_field :email %>

8 | 9 |

<%= f.label :password %> (leave blank if you don't want to change it)
10 | <%= f.password_field :password %>

11 | 12 |

<%= f.label :password_confirmation %>
13 | <%= f.password_field :password_confirmation %>

14 | 15 |

<%= f.label :current_password %> (we need your current password to confirm your changes)
16 | <%= f.password_field :current_password %>

17 | 18 |

<%= f.submit "Update" %>

19 | <% end %> 20 | 21 |

Cancel my account

22 | 23 |

Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), :confirm => "Are you sure?", :method => :delete %>.

24 | 25 | <%= link_to "Back", :back %> 26 | -------------------------------------------------------------------------------- /app/views/templates/_form.html.haml: -------------------------------------------------------------------------------- 1 | %div{:style => "display: none;"} 2 | #zone-name= t :help_template_name 3 | #zone-ttl 4 | 5 | = form_for( @zone_template ) do |f| 6 | = error_messages_for :zone_template 7 | %table 8 | %tr 9 | %td= t :label_template_zone_name 10 | %td   11 | %td= f.text_field :name 12 | %tr 13 | %td= t :label_template_zone_ttl 14 | %td   15 | %td= f.text_field :ttl, :size => 6 16 | %tr 17 | %td= t :label_template_zone_type 18 | %td   19 | %td= f.select :type, ['NATIVE','MASTER','SLAVE'] 20 | %tr 21 | %td= t :label_template_zone_master 22 | %td   23 | %td= f.text_field :master, :size => 15 24 | - if User.active_owners.any? 25 | %tr 26 | %td= t :label_template_zone_owner 27 | %td   28 | %td= f.collection_select :user_id, User.active_owners, :id, :login, :include_blank => true 29 | %tr 30 | %td= link_to_cancel @zone_template 31 | %td= submit_tag t(:generic_save) 32 | -------------------------------------------------------------------------------- /TODO.textile: -------------------------------------------------------------------------------- 1 | h1. PowerDNS on Rails TODO 2 | 3 | Simple list of things to do by version. This is not authorative, the real list will be maintained at the LightHouse tracker. 4 | 5 | http://kennethkalmer.lighthouseapp.com/projects/11831-powerdns-on-rails/ 6 | 7 | h2. Not Started / In Progress 8 | 9 | * Multiple users (admins or owners) 10 | (remove roles model and persist roles in users table) 11 | * Solid models and model specs 12 | * Documentation (becoming urgent) 13 | * Configuration samples for powerdns 14 | * Basic API client 15 | * DNS Insights 16 | * API improvements 17 | * Deployment instructions directly from GitHub 18 | * mod_passenger installation 19 | * simple git clone and future git pull's 20 | * Import scripts for standard zone files and PowerDNS 21 | * Flexible model support? 22 | * Statistics (import pdns logs) 23 | 24 | h2. Completed 25 | 26 | * Github project page 27 | * Configurable macros 28 | * Basic database structure 29 | * Zone/Record Templates 30 | * Authentication tokens for one-time access 31 | * User management 32 | -------------------------------------------------------------------------------- /app/views/reports/index.html.haml: -------------------------------------------------------------------------------- 1 | %h2.underline= t :title_user_reports 2 | 3 | #search-user-bar.padded 4 | - form_tag( { :controller => :reports, :action => :results }, { :method => :get } ) do 5 | = text_field_tag :q 6 | = submit_tag t(:label_user_search) 7 | 8 | #users.padded 9 | %table.grid 10 | %tr 11 | %th.left= t :label_user_login 12 | %th.right= t :label_user_domains 13 | %th.left= t :label_user_email 14 | - if @users 15 | = render :partial => 'user', :collection => @users 16 | - else 17 | %tr 18 | %td 19 | %em= t :message_user_no_domains 20 | %td.right 0 21 | %td - 22 | %tr 23 | %td{ :colspan => 3 }   24 | %tr 25 | %td 26 | %em= t :label_report_system_domains 27 | %td.right= @system_domains 28 | %td   29 | %tr 30 | %td 31 | %em= t :label_report_total_domains 32 | %td.right= @total_domains 33 | %td   34 | 35 | = will_paginate @users if @users 36 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | gem 'rails', '~> 3.2.21' 4 | 5 | group :assets do 6 | gem 'sass-rails' 7 | gem 'coffee-rails' 8 | gem 'uglifier' 9 | end 10 | 11 | platforms :ruby do 12 | gem 'mysql2', '~> 0.3.11' 13 | gem 'pg', '>= 0.9.0' 14 | gem 'sqlite3' 15 | gem 'therubyracer' 16 | end 17 | 18 | gem 'haml-rails' 19 | gem 'jquery-rails' 20 | gem 'will_paginate', '~> 3.0.4' 21 | gem "audited-activerecord", "~> 3.0.0.rc2" 22 | gem 'inherited_resources' 23 | gem 'devise', '~> 2.2.8' 24 | gem "devise-encryptable" 25 | gem 'rabl' 26 | gem 'state_machine' 27 | 28 | gem 'acts_as_list' 29 | gem 'dynamic_form' 30 | 31 | group :development do 32 | gem 'debugger', :platform => :mri_19 33 | #gem 'RedCloth', '>= 4.1.1' 34 | end 35 | 36 | group :development, :test do 37 | gem "rspec-rails" 38 | gem 'RedCloth', '>= 4.1.1' 39 | end 40 | 41 | group :test do 42 | gem "factory_girl_rails", "~> 4.0" 43 | 44 | gem "cucumber-rails", :require => false 45 | gem 'mocha', :require => false 46 | gem 'webrat' 47 | gem 'database_cleaner' 48 | end 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Kenneth Kalmer 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /features/support/selectors.rb: -------------------------------------------------------------------------------- 1 | module HtmlSelectorsHelpers 2 | # Maps a name to a selector. Used primarily by the 3 | # 4 | # When /^(.+) within (.+)$/ do |step, scope| 5 | # 6 | # step definitions in web_steps.rb 7 | # 8 | def selector_for(locator) 9 | case locator 10 | 11 | when "the page" 12 | "html > body" 13 | 14 | # Add more mappings here. 15 | # Here is an example that pulls values out of the Regexp: 16 | # 17 | # when /^the (notice|error|info) flash$/ 18 | # ".flash.#{$1}" 19 | 20 | # You can also return an array to use a different selector 21 | # type, like: 22 | # 23 | # when /the header/ 24 | # [:xpath, "//header"] 25 | 26 | # This allows you to provide a quoted selector as the scope 27 | # for "within" steps as was previously the default for the 28 | # web steps: 29 | when /^"(.+)"$/ 30 | $1 31 | 32 | else 33 | raise "Can't find mapping from \"#{locator}\" to a selector.\n" + 34 | "Now, go and add a mapping in #{__FILE__}" 35 | end 36 | end 37 | end 38 | 39 | World(HtmlSelectorsHelpers) 40 | -------------------------------------------------------------------------------- /spec/views/reports/results.html.haml_spec: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../../spec_helper' 2 | 3 | describe "/reports/results" do 4 | 5 | before(:each) do 6 | @admin = FactoryGirl.create(:admin) 7 | end 8 | 9 | it "should handle no results" do 10 | assigns[:results] = [] 11 | 12 | render "/reports/results" 13 | 14 | response.should have_tag("strong", "No users found") 15 | end 16 | 17 | it "should handle results within the pagination limit" do 18 | assigns[:results] = User.search( 'a', 1 ) 19 | 20 | render "/reports/results" 21 | 22 | response.should have_tag("table") do 23 | with_tag "a", "admin" 24 | end 25 | end 26 | 27 | it "should handle results with pagination and scoping" do 28 | 1.upto(100) do |i| 29 | user = User.new 30 | user.login = "test-user-#{i}" 31 | user.save( false ).should be_true 32 | end 33 | 34 | assigns[:results] = User.search( 'test-user', 1 ) 35 | 36 | render "/reports/results" 37 | 38 | response.should have_tag("table") do 39 | with_tag "a", "test-user-1" 40 | end 41 | end 42 | 43 | end 44 | -------------------------------------------------------------------------------- /app/views/macros/edit.html.haml: -------------------------------------------------------------------------------- 1 | %h1.underline 2 | = @macro.new_record? ? t(:title_macro_new) : t(:title_macro_update) 3 | 4 | %div{ :style => "display:none" } 5 | #macro-name= t :help_macro_name 6 | #macro-active= t :help_macro_active 7 | 8 | = form_for( @macro ) do |f| 9 | = error_messages_for :macro 10 | 11 | %table 12 | %tr 13 | %td= t(:label_macro_name) 14 | %td   15 | %td 16 | = f.text_field :name 17 | = help_icon('macro-name') 18 | %tr 19 | %td.top= t(:label_macro_description) 20 | %td   21 | %td= f.text_area :description, :size => '35x2' 22 | - if current_user.admin? && !possible_owners.empty? 23 | %tr 24 | %td= t(:label_macro_owner) 25 | %td   26 | %td= f.collection_select :user_id, possible_owners, :id, :login, :include_blank => true 27 | %tr 28 | %td= t(:label_macro_active) 29 | %td   30 | %td 31 | = f.check_box :active 32 | = help_icon('macro-active') 33 | %tr 34 | %td.right= link_to_cancel @macro 35 | %td   36 | %td= submit_tag t(:generic_save) 37 | -------------------------------------------------------------------------------- /spec/views/macros/edit.html.haml_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "macros/edit.html.haml" do 4 | before(:each) do 5 | admin = FactoryGirl.build_stubbed( :admin ) 6 | view.stub( :current_user ).and_return( admin ) 7 | end 8 | 9 | context "for new macros" do 10 | before(:each) do 11 | assign(:macro, Macro.new) 12 | render 13 | end 14 | 15 | it "should behave accordingly" do 16 | rendered.should have_tag('h1', :content => 'New Macro') 17 | end 18 | 19 | end 20 | 21 | context "for existing records" do 22 | before(:each) do 23 | @macro = FactoryGirl.create(:macro) 24 | assign(:macro, @macro) 25 | render 26 | end 27 | 28 | it "should behave accordingly" do 29 | rendered.should have_tag('h1', :content => 'Update Macro') 30 | end 31 | end 32 | 33 | describe "for records with errors" do 34 | before(:each) do 35 | m = Macro.new 36 | m.valid? 37 | assign(:macro, m) 38 | render 39 | end 40 | 41 | it "should display the errors" do 42 | rendered.should have_tag('div.errorExplanation') 43 | end 44 | end 45 | 46 | end 47 | -------------------------------------------------------------------------------- /app/views/domains/edit.html.haml: -------------------------------------------------------------------------------- 1 | %h1.underline=t :title_update_zone 2 | 3 | / These divs contain the help information. 4 | %div{ :style => 'display: none' } 5 | #help-domain=t :help_domain_fqdn 6 | #help-type=t :help_domain_type 7 | #help-master=t :help_domain_master 8 | #help-ttl=t :help_ttl 9 | 10 | = form_for(@domain) do |f| 11 | = error_messages_for :domain 12 | %p   13 | %table.grid 14 | %tr 15 | %td{:width => "300"}=t :label_domain_name 16 | %td= f.text_field :name 17 | %td= help_icon('help-domain') 18 | %tr 19 | %td= t :label_domain_type 20 | %td 21 | = f.select :type, ['NATIVE','MASTER','SLAVE'] 22 | %td= help_icon('help-type') 23 | %tr#master-address{ :style => 'display: none' } 24 | %td= t :label_domain_master_address 25 | %td= f.text_field :master 26 | %td= help_icon('help-master') 27 | %tr 28 | %td= t :label_default_ttl 29 | %td= f.text_field :ttl, :size => 8 30 | %td= help_icon('help-ttl') 31 | %table.grid 32 | %tr 33 | %td.right{:width => "300"}= link_to_cancel @domain 34 | %td= submit_tag t(:generic_save) 35 | %td   36 | 37 | -------------------------------------------------------------------------------- /config/database.yml.template: -------------------------------------------------------------------------------- 1 | # You'll need to configure this file for the appropriate environment before 2 | # you run PowerDNS on Rails. 3 | 4 | sqlite: &sqlite 5 | adapter: sqlite3 6 | database: db/<%= Rails.env %>.sqlite3 7 | 8 | mysql: &mysql 9 | adapter: mysql2 10 | username: root 11 | password: 12 | database: powerdns_<%= Rails.env %> 13 | encoding: utf8 14 | 15 | postgresql: &postgresql 16 | adapter: postgresql 17 | encoding: unicode 18 | username: postgres 19 | password: 20 | database: powerdns_<%= Rails.env %> 21 | min_messages: ERROR 22 | 23 | defaults: &defaults 24 | pool: 16 25 | timeout: 5000 26 | host: localhost 27 | <<: *<%= ENV['DB'] || "mysql" %> 28 | 29 | # Development, defaults to MySQL 30 | development: 31 | <<: *defaults 32 | 33 | # Warning: The database defined as 'test' will be erased and 34 | # re-generated from your development database when you run 'rake'. 35 | # Do not set this db to the same as development or production. 36 | test: 37 | <<: *defaults 38 | 39 | production: 40 | adapter: mysql2 41 | database: powerdns_production 42 | host: localhost 43 | username: root 44 | password: dont-use-root-here 45 | 46 | -------------------------------------------------------------------------------- /spec/factories/zone_template_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | 3 | factory :zone_template, :class => ZoneTemplate do 4 | name 'East Coast Data Center' 5 | ttl 86400 6 | end 7 | 8 | factory :template_soa, :class => RecordTemplate do 9 | ttl 86400 10 | record_type 'SOA' 11 | #content 'ns1.%ZONE% admin@%ZONE% 0 10800 7200 604800 3600' 12 | primary_ns 'ns1.%ZONE%' 13 | contact 'admin@%ZONE%' 14 | refresh 10800 15 | self.retry 7200 16 | expire 604800 17 | minimum 3600 18 | end 19 | 20 | factory :template_ns, :class => RecordTemplate do 21 | ttl 86400 22 | record_type 'NS' 23 | content 'ns1.%ZONE%' 24 | end 25 | 26 | factory :template_ns_a, :class => RecordTemplate do 27 | ttl 86400 28 | record_type 'A' 29 | name 'ns1.%ZONE%' 30 | content '10.0.0.1' 31 | end 32 | 33 | factory :template_cname, :class => RecordTemplate do 34 | ttl 86400 35 | record_type 'CNAME' 36 | name '%ZONE%' 37 | content 'some.cname.org' 38 | end 39 | 40 | factory :template_mx, :class => RecordTemplate do 41 | ttl 86400 42 | record_type 'MX' 43 | content 'mail.%ZONE%' 44 | prio 10 45 | end 46 | 47 | end 48 | -------------------------------------------------------------------------------- /app/views/users/show.html.haml: -------------------------------------------------------------------------------- 1 | %h1.underline 2 | = t(:title_user_detail_for, :user => @user.login) 3 | 4 | %table.grid 5 | %tr 6 | %td= t :label_user_name 7 | %td= @user.login 8 | %tr 9 | %td= t :label_user_email 10 | %td= @user.email 11 | %tr 12 | %td= t :label_user_admin 13 | %td= @user.admin? ? 'Yes' : 'No' 14 | %tr 15 | %td= t :label_user_auth_tokens 16 | %td= @user.auth_tokens? ? 'Yes' : 'No' 17 | %tr 18 | %td   19 | %td   20 | %tr 21 | %td   22 | %td 23 | = link_to image_tag('user_edit.png'), edit_user_path( @user ) 24 | = link_to t(:generic_change), edit_user_path( @user ) 25 |     26 | = link_to image_tag('user_delete.png'), user_path( @user ), :method => :delete, :confirm => t(:confirm_user_remove) 27 | = link_to t(:generic_remove), user_path( @user ), :method => :delete, :confirm => t(:confirm_user_remove) 28 | - unless @user.admin? 29 |     30 | = link_to image_tag('help.png') , :controller => :reports , :action => :view , :id => @user 31 | = link_to t(:label_user_detail) , :controller => :reports , :action => :view , :id => @user 32 | -------------------------------------------------------------------------------- /spec/controllers/sessions_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe SessionsController, "and auth tokens" do 4 | 5 | before(:each) do 6 | @domain = FactoryGirl.create(:domain) 7 | @user = FactoryGirl.create(:admin) 8 | @token = FactoryGirl.create(:auth_token, :domain => @domain, :user => @user) 9 | end 10 | 11 | xit 'accepts and redirects' do 12 | post :token, :token => '5zuld3g9dv76yosy' 13 | session[:token_id].should_not be_nil 14 | controller.send(:token_user?).should be_true 15 | response.should be_redirect 16 | response.should redirect_to( domain_path( @domain ) ) 17 | end 18 | 19 | xit 'fails login and does not redirect' do 20 | post :token, :token => 'bad_token' 21 | session[:token_id].should be_nil 22 | response.should be_success 23 | end 24 | 25 | xit 'logs out' do 26 | tokenize_as(@token) 27 | get :destroy 28 | session[:token_id].should be_nil 29 | response.should redirect_to( session_path ) 30 | end 31 | 32 | xit 'fails expired cookie login' do 33 | @token.update_attribute :expires_at, 5.minutes.ago 34 | get :new 35 | controller.send(:token_user?).should_not be_true 36 | end 37 | 38 | end 39 | -------------------------------------------------------------------------------- /app/views/devise/shared/_links.erb: -------------------------------------------------------------------------------- 1 | <%- if controller_name != 'sessions' %> 2 | <%= link_to "Sign in", new_session_path(resource_name) %>
3 | <% end -%> 4 | 5 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %> 6 | <%= link_to "Sign up", new_registration_path(resource_name) %>
7 | <% end -%> 8 | 9 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' %> 10 | <%= link_to "Forgot your password?", new_password_path(resource_name) %>
11 | <% end -%> 12 | 13 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> 14 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
15 | <% end -%> 16 | 17 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> 18 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
19 | <% end -%> 20 | 21 | <%- if devise_mapping.omniauthable? %> 22 | <%- resource_class.omniauth_providers.each do |provider| %> 23 | <%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider) %>
24 | <% end -%> 25 | <% end -%> -------------------------------------------------------------------------------- /spec/models/a_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe A do 4 | 5 | context "new record" do 6 | 7 | it "should be invalid by default" do 8 | subject.should_not be_valid 9 | end 10 | 11 | it "should only accept valid IPv4 addresses as content" do 12 | subject.content = '10' 13 | subject.should have(1).error_on(:content) 14 | 15 | subject.content = '10.0' 16 | subject.should have(1).error_on(:content) 17 | 18 | subject.content = '10.0.0' 19 | subject.should have(1).error_on(:content) 20 | 21 | subject.content = '10.0.0.9/32' 22 | subject.should have(1).error_on(:content) 23 | 24 | subject.content = '256.256.256.256' 25 | subject.should have(1).error_on(:content) 26 | 27 | subject.content = '10.0.0.9' 28 | subject.should have(:no).error_on(:content) 29 | end 30 | 31 | it "should not accept new lines in content" do 32 | subject.content = "10.1.1.1\nHELLO WORLD" 33 | subject.should have(1).error_on(:content) 34 | end 35 | 36 | it "should not act as a CNAME" do 37 | subject.content = 'google.com' 38 | subject.should have(1).error_on(:content) 39 | end 40 | 41 | end 42 | 43 | end 44 | -------------------------------------------------------------------------------- /app/models/ns.rb: -------------------------------------------------------------------------------- 1 | # See #NS 2 | 3 | # = Name Server Record (NS) 4 | # 5 | # Defined in RFC 1035. NS RRs appear in two places. Within the zone file, in 6 | # which case they are authoritative records for the zone's name servers. At the 7 | # point of delegation for either a subdomain of the zone or in the zone's 8 | # parent. Thus the zone example.com's parent zone (.com) will contain 9 | # non-authoritative NS RRs for the zone example.com at its point of delegation 10 | # and subdomain.example.com will have non-authoritative NS RSS in the zone 11 | # example.com at its point of delegation. NS RRs at the point of delegation are 12 | # never authoritative only NS RRs for the zone are regarded as authoritative. 13 | # While this may look a fairly trivial point, is has important implications for 14 | # DNSSEC. 15 | # 16 | # NS RRs are required because DNS queries respond with an authority section 17 | # listing all the authoritative name servers, for sub-domains or queries to the 18 | # zones parent where they are required to allow referral to take place. 19 | # 20 | # Obtained from http://www.zytrax.com/books/dns/ch8/ns.html 21 | # 22 | class NS < Record 23 | 24 | validates :content, :presence => true, :hostname => true 25 | 26 | end 27 | -------------------------------------------------------------------------------- /spec/controllers/record_template_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RecordTemplatesController, "when updating SOA records" do 4 | before(:each) do 5 | sign_in(FactoryGirl.create(:admin)) 6 | @zt = FactoryGirl.create(:zone_template) 7 | end 8 | 9 | it "should create valid templates" do 10 | expect { 11 | xhr :post, :create, :record_template => { 12 | :retry => "7200", :primary_ns => 'ns1.provider.net', 13 | :contact => 'east-coast@example.com', :refresh => "10800", :minimum => "10800", 14 | :expire => "604800", :record_type => "SOA" 15 | }, :zone_template => { :id => @zt.id } 16 | 17 | }.to change( RecordTemplate, :count ).by(1) 18 | end 19 | 20 | it "should accept a valid update" do 21 | target_soa = FactoryGirl.create(:template_soa, :zone_template => @zt) 22 | 23 | xhr :put, :update, :id => target_soa.id, :record_template => { 24 | :retry => "7200", :primary_ns => 'ns1.provider.net', 25 | :contact => 'east-coast@example.com', :refresh => "10800", :minimum => "10800", 26 | :expire => "604800" 27 | } 28 | 29 | target_soa.reload 30 | target_soa.primary_ns.should eql('ns1.provider.net') 31 | end 32 | 33 | end 34 | -------------------------------------------------------------------------------- /spec/views/macro_steps/create.js.rjs_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "macro_steps/create.js.rjs" do 4 | describe "for failed records" do 5 | before(:each) do 6 | assigns[:macro] = Factory.build(:macro) 7 | assigns[:macro_step] = MacroStep.new 8 | assigns[:macro_step].valid? 9 | 10 | render('macro_steps/create.js.rjs') 11 | end 12 | 13 | xit "should insert errors into the page" do 14 | response.should have_rjs(:replace_html, 'record-form-error') 15 | end 16 | 17 | xit "should have a error flash" do 18 | response.should include_text(%{showflash("error"}) 19 | end 20 | end 21 | 22 | describe "for successful records" do 23 | before(:each) do 24 | assigns[:macro] = FactoryGirl.create(:macro) 25 | assigns[:macro_step] = FactoryGirl.create(:macro_step_create, :macro => assigns[:macro]) 26 | 27 | render('macro_steps/create.js.rjs') 28 | end 29 | 30 | xit "should display a notice flash" do 31 | response.should include_text(%{showflash("info"} ) 32 | end 33 | 34 | xit "should insert the steps into the table" do 35 | response.should have_rjs(:insert, :bottom, 'steps-table') 36 | end 37 | 38 | end 39 | 40 | end 41 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | PowerdnsOnRails::Application.routes.draw do 2 | devise_for :users, :controllers => { :sessions => "sessions" }, :path => "sessions" 3 | 4 | root :to => 'dashboard#index' 5 | 6 | resources :domains do 7 | member do 8 | put :change_owner 9 | get :apply_macro 10 | post :apply_macro 11 | put :update_note 12 | end 13 | 14 | resources :records do 15 | member do 16 | put :update_soa 17 | end 18 | end 19 | end 20 | 21 | resources :zone_templates, :controller => 'templates' 22 | resources :record_templates 23 | 24 | resources :macros do 25 | resources :macro_steps 26 | end 27 | 28 | match '/audits(/:action(/:id))' => 'audits#index', :as => :audits 29 | match '/reports(/:action)' => 'reports#index', :as => :reports 30 | 31 | resource :auth_token 32 | post '/token/:token' => 'sessions#token', :as => :token 33 | 34 | resources :users do 35 | member do 36 | put :suspend 37 | put :unsuspend 38 | delete :purge 39 | end 40 | end 41 | 42 | get '/search(/:action)' => 'search#results', :as => :search 43 | 44 | #resource :session 45 | #match '/logout' => 'sessions#destroy', :as => :logout 46 | #match '/:controller(/:action(/:id))' 47 | end 48 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to spec/ when you run 'rails generate rspec:install' 2 | ENV["RAILS_ENV"] ||= 'test' 3 | require File.expand_path("../../config/environment", __FILE__) 4 | require 'rspec/rails' 5 | 6 | # Requires supporting ruby files with custom matchers and macros, etc, 7 | # in spec/support/ and its subdirectories. 8 | Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} 9 | 10 | RSpec.configure do |config| 11 | # == Mock Framework 12 | # 13 | # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: 14 | # 15 | #config.mock_with :mocha 16 | # config.mock_with :flexmock 17 | # config.mock_with :rr 18 | config.mock_with :rspec 19 | 20 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures 21 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 22 | 23 | # If you're not using ActiveRecord, or you'd prefer not to run each of your 24 | # examples within a transaction, remove the following line or assign false 25 | # instead of true. 26 | config.use_transactional_fixtures = true 27 | 28 | config.include Devise::TestHelpers, :type => :controller 29 | config.include SignInHelpers, :type => :controller 30 | config.include Webrat::HaveTagMatcher 31 | end 32 | -------------------------------------------------------------------------------- /script/provision: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | set -e 5 | 6 | # Back to the future 7 | sudo apt-get update -q 8 | 9 | # Support for Brightbox's ruby-ng ppa 10 | sudo apt-get install -y python-software-properties 11 | sudo apt-add-repository ppa:brightbox/ruby-ng 12 | sudo apt-get update -q 13 | 14 | # Dependencies 15 | sudo apt-get install -yq build-essential ssl-cert \ 16 | libsqlite3-dev libsqlite3-0 \ 17 | libpq5 libpq-dev \ 18 | libmysqlclient-dev \ 19 | ruby2.1 ruby2.1-dev 20 | 21 | # Disable documentation generation for gems 22 | echo 'gem: --no-rdoc --no-ri' >> ~/.gemrc 23 | sudo gem install bundler 24 | 25 | # Application itself 26 | cd /vagrant 27 | bundle install 28 | 29 | # Database setup 30 | if [[ ! -f config/database.yml ]]; then 31 | cp config/database.yml.template config/database.yml 32 | fi 33 | 34 | # Tokens 35 | rake generate_secret_token 36 | 37 | # Bootstrap 38 | DB=sqlite rake db:setup 39 | 40 | # And run a daemon 41 | DB=sqlite script/rails s -d 42 | 43 | set +x 44 | 45 | echo "-" 46 | echo "-" 47 | echo "- We're all done here! You can now visit http://localhost:8080 in your browser" 48 | echo "- to play with a demo of powerdns-on-rails." 49 | echo "-" 50 | echo "-" 51 | echo "- Please refer to the README for more information." 52 | echo "-" 53 | echo "-" 54 | -------------------------------------------------------------------------------- /spec/views/macro_steps/update.js.rjs_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "macro_steps/update.js.rjs" do 4 | before(:each) do 5 | assigns[:macro] = @macro = FactoryGirl.create(:macro) 6 | assigns[:macro_step] = @macro_step = FactoryGirl.create(:macro_step_create, :macro => @macro) 7 | end 8 | 9 | describe "for valid updates" do 10 | 11 | before(:each) do 12 | render "macro_steps/update.js.rjs" 13 | end 14 | 15 | xit "should display a notice" do 16 | response.should include_text(%{showflash("info"}) 17 | end 18 | 19 | xit "should update the steps table" do 20 | response.should have_rjs(:remove, "show_macro_step_#{@macro_step.id}") 21 | response.should have_rjs(:remove, "edit_macro_step_#{@macro_step.id}") 22 | response.should have_rjs(:replace, "marker_macro_step_#{@macro_step.id}") 23 | end 24 | 25 | end 26 | 27 | describe "for invalid updates" do 28 | 29 | before(:each) do 30 | assigns[:macro_step].content = '' 31 | assigns[:macro_step].valid? 32 | 33 | render "macro_steps/update.js.rjs" 34 | end 35 | 36 | 37 | xit "should display an error" do 38 | response.should have_rjs(:replace_html, "error_macro_step_#{@macro_step.id}") 39 | end 40 | 41 | end 42 | end 43 | 44 | -------------------------------------------------------------------------------- /spec/models/mx_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe MX do 4 | context "when new" do 5 | 6 | it "should be invalid by default" do 7 | subject.should_not be_valid 8 | end 9 | 10 | it "should require a priority" do 11 | subject.should have(1).error_on(:prio) 12 | end 13 | 14 | it "should only allow positive, numeric priorities, between 0 and 65535 (inclusive)" do 15 | subject.prio = -10 16 | subject.should have(1).error_on(:prio) 17 | 18 | subject.prio = 65536 19 | subject.should have(1).error_on(:prio) 20 | 21 | subject.prio = 'low' 22 | subject.should have(1).error_on(:prio) 23 | 24 | subject.prio = 10 25 | subject.should have(:no).errors_on(:prio) 26 | end 27 | 28 | it "should require content" do 29 | subject.should have(2).error_on(:content) 30 | end 31 | 32 | it "should not accept IP addresses as content" do 33 | subject.content = "127.0.0.1" 34 | subject.should have(1).error_on(:content) 35 | end 36 | 37 | it "should not accept spaces in content" do 38 | subject.content = 'spaced out.com' 39 | subject.should have(1).error_on(:content) 40 | end 41 | 42 | it "should support priorities" do 43 | subject.supports_prio?.should be_true 44 | end 45 | 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /config/environments/cucumber.rb: -------------------------------------------------------------------------------- 1 | config.cache_classes = true # This must be true for Cucumber to operate correctly! 2 | 3 | # Log error messages when you accidentally call methods on nil. 4 | config.whiny_nils = true 5 | 6 | # Show full error reports and disable caching 7 | config.action_controller.consider_all_requests_local = true 8 | config.action_controller.perform_caching = false 9 | 10 | # Disable request forgery protection in test environment 11 | config.action_controller.allow_forgery_protection = false 12 | 13 | # Tell Action Mailer not to deliver emails to the real world. 14 | # The :test delivery method accumulates sent emails in the 15 | # ActionMailer::Base.deliveries array. 16 | config.action_mailer.delivery_method = :test 17 | 18 | config.gem "cucumber", :lib => false, :version => ">=0.3.11" unless File.directory?(File.join(Rails.root, 'vendor/plugins/cucumber')) 19 | config.gem "webrat", :lib => false, :version => ">=0.4.4" unless File.directory?(File.join(Rails.root, 'vendor/plugins/webrat')) 20 | config.gem "rspec", :lib => false, :version => ">=1.2.6" unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec')) 21 | config.gem "rspec-rails", :lib => 'spec/rails', :version => ">=1.2.6" unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec-rails')) 22 | -------------------------------------------------------------------------------- /spec/views/search/results.html.haml_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "search/results.html.haml" do 4 | 5 | before(:each) do 6 | @admin = FactoryGirl.create(:admin) 7 | view.stubs(:current_user).returns(@admin) 8 | view.stubs(:current_token).returns(nil) 9 | end 10 | 11 | it "should handle no results" do 12 | assign(:results, []) 13 | 14 | render 15 | 16 | rendered.should have_tag("strong", :content => "No domains found") 17 | end 18 | 19 | it "should handle results within the pagination limit" do 20 | 1.upto(4) do |i| 21 | zone = Domain.new 22 | zone.id = i 23 | zone.name = "zone-#{i}.com" 24 | zone.save( :validate => false ).should be_true 25 | end 26 | 27 | assign(:results, Domain.search( 'zone', 1, @admin )) 28 | 29 | render 30 | 31 | rendered.should have_tag("table a", :content => "zone-1.com") 32 | end 33 | 34 | it "should handle results with pagination and scoping" do 35 | 1.upto(100) do |i| 36 | zone = Domain.new 37 | zone.id = i 38 | zone.name = "domain-#{i}.com" 39 | zone.save( :validate => false ).should be_true 40 | end 41 | 42 | assign(:results, Domain.search( 'domain', 1, @admin )) 43 | 44 | render 45 | 46 | rendered.should have_tag("table a", :content => "domain-1.com") 47 | end 48 | 49 | end 50 | -------------------------------------------------------------------------------- /spec/factories/users_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | 3 | factory :admin, :class => User do 4 | login 'admin' 5 | email 'admin@example.com' 6 | password 'secret' 7 | password_confirmation 'secret' 8 | confirmation_token nil 9 | confirmed_at Time.now 10 | admin true 11 | end 12 | 13 | factory :quentin, :class => User do 14 | login 'quentin' 15 | email 'quentin@example.com' 16 | password 'secret' 17 | password_confirmation 'secret' 18 | confirmation_token nil 19 | confirmed_at Time.now 20 | end 21 | 22 | factory :aaron, :class => User do 23 | login 'aaron' 24 | email 'aaron@example.com' 25 | password 'secret' 26 | password_confirmation 'secret' 27 | confirmation_token nil 28 | confirmed_at Time.now 29 | end 30 | 31 | factory :token_user, :class => User do 32 | login 'token' 33 | email 'token@example.com' 34 | password 'secret' 35 | password_confirmation 'secret' 36 | admin true 37 | auth_tokens true 38 | confirmation_token nil 39 | confirmed_at Time.now 40 | end 41 | 42 | factory :api_client, :class => User do 43 | login 'api' 44 | email 'api@example.com' 45 | password 'secret' 46 | password_confirmation 'secret' 47 | admin true 48 | confirmation_token nil 49 | confirmed_at Time.now 50 | end 51 | 52 | end 53 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | # General Apache options 2 | AddHandler fastcgi-script .fcgi 3 | AddHandler cgi-script .cgi 4 | Options +FollowSymLinks +ExecCGI 5 | 6 | # If you don't want Rails to look in certain directories, 7 | # use the following rewrite rules so that Apache won't rewrite certain requests 8 | # 9 | # Example: 10 | # RewriteCond %{REQUEST_URI} ^/notrails.* 11 | # RewriteRule .* - [L] 12 | 13 | # Redirect all requests not available on the filesystem to Rails 14 | # By default the cgi dispatcher is used which is very slow 15 | # 16 | # For better performance replace the dispatcher with the fastcgi one 17 | # 18 | # Example: 19 | # RewriteRule ^(.*)$ dispatch.fcgi [QSA,L] 20 | RewriteEngine On 21 | 22 | # If your Rails application is accessed via an Alias directive, 23 | # then you MUST also set the RewriteBase in this htaccess file. 24 | # 25 | # Example: 26 | # Alias /myrailsapp /path/to/myrailsapp/public 27 | # RewriteBase /myrailsapp 28 | 29 | RewriteRule ^$ index.html [QSA] 30 | RewriteRule ^([^.]+)$ $1.html [QSA] 31 | RewriteCond %{REQUEST_FILENAME} !-f 32 | RewriteRule ^(.*)$ dispatch.cgi [QSA,L] 33 | 34 | # In case Rails experiences terminal errors 35 | # Instead of displaying this message you can supply a file here which will be rendered instead 36 | # 37 | # Example: 38 | # ErrorDocument 500 /500.html 39 | 40 | ErrorDocument 500 "

Application error

Rails application failed to start properly" 41 | -------------------------------------------------------------------------------- /app/views/users/_form.html.haml: -------------------------------------------------------------------------------- 1 | %div{:style => "display: none"} 2 | #admin-user= t :help_user_is_admin 3 | #domain-owner= t :help_user_is_user 4 | #token-user-help= t :help_user_token_info 5 | 6 | = form_for( @user ) do |f| 7 | = error_messages_for( :user ) 8 | 9 | %table 10 | %tr 11 | %td= t :label_user_name 12 | %td= f.text_field :login 13 | %tr 14 | %td= t :label_user_email 15 | %td= f.text_field :email 16 | %tr 17 | %td.top= t :label_user_password 18 | %td 19 | = f.password_field :password 20 | - unless @user.new_record? 21 | %br 22 | %em= t :text_user_password_edit_notice 23 | %tr 24 | %td.top= t :label_user_password_again 25 | %td= f.password_field :password_confirmation 26 | %tr 27 | %td.top= t :label_user_role_sign 28 | %td 29 | = f.radio_button :admin, true 30 | = t "label_user_roles.admin" 31 | = help_icon('admin-user') 32 | %br 33 | = f.radio_button :admin, false 34 | = t "label_user_roles.domain_owner" 35 | = help_icon('domain-owner') 36 | %br 37 | = f.check_box 'auth_tokens' 38 | = t :text_user_manage_token 39 | = help_icon('token-user-help') 40 | %tr 41 | %td   42 | %td   43 | %tr 44 | %td.right= link_to t(:generic_cancel), users_path 45 | %td= submit_tag t(:generic_save) 46 | 47 | -------------------------------------------------------------------------------- /app/views/users/_user.html.haml: -------------------------------------------------------------------------------- 1 | %div{ :style => "display: none;" } 2 | %div{ :id => "new-user-#{user.id}" }= t :help_user_create 3 | %div{ :id => "active-user-#{user.id}" }= t :help_user_is_active 4 | %div{ :id => "suspended-user-#{user.id}" }= t :help_user_is_suspended 5 | %div{ :id => "edit-user-#{user.id}" }= t :help_user_details_edit 6 | %div{ :id => "suspend-user-#{user.id}" }= t :help_user_suspend 7 | %div{ :id => "reactivate-user-#{user.id}" }= t :help_user_activate 8 | %div{ :id => "delete-user-#{user.id}" }= t :help_user_delete 9 | 10 | %tr[ user, :show ] 11 | %td= link_to user.login, user_path( user ) 12 | %td= user.email 13 | %td= user.admin? ? t("label_user_role.admin") : t("label_user_role.user") 14 | %td 15 | - unless user.suspended? 16 | = info_icon('flag_green.png', "active-user-#{user.id}") 17 | - else 18 | = info_icon('flag_orange.png', "suspended-user-#{user.id}") 19 | %td 20 | = link_to info_icon('user_edit.png', "edit-user-#{user.id}"), edit_user_path( user ) 21 | - unless user.suspended? 22 | = link_to info_icon('user_suspend.png', "suspend-user-#{user.id}"), suspend_user_path( user ), :method => :put unless user.eql?(current_user) 23 | - else 24 | = link_to info_icon('user_go.png', "reactivate-user-#{user.id}"), unsuspend_user_path( user ), :method => :put 25 | = link_to info_icon('user_delete.png', "delete-user-#{user.id}"), purge_user_path( user ), :method => :delete unless user.eql?(current_user) 26 | -------------------------------------------------------------------------------- /app/helpers/audits_helper.rb: -------------------------------------------------------------------------------- 1 | module AuditsHelper 2 | 3 | def parenthesize( text ) 4 | "(#{text})" 5 | end 6 | 7 | def link_to_domain_audit( audit ) 8 | caption = "#{audit.version} #{audit.action} by " 9 | caption << audit_user( audit ) 10 | link_to_function caption, "toggleDomainAudit(#{audit.id})" 11 | end 12 | 13 | def link_to_record_audit( audit ) 14 | caption = audit.audited_changes['type'] 15 | caption ||= (audit.auditable.nil? ? '[UNKNOWN]' : audit.auditable.class.to_s ) 16 | unless audit.audited_changes['name'].nil? 17 | name = audit.audited_changes['name'].is_a?( Array ) ? audit.audited_changes['name'].pop : audit.audited_changes['name'] 18 | caption += " (#{name})" 19 | end 20 | caption += " #{audit.version} #{audit.action} by " 21 | caption += audit_user( audit ) 22 | link_to_function caption, "toggleRecordAudit(#{audit.id})" 23 | end 24 | 25 | def display_hash( hash ) 26 | hash ||= {} 27 | hash.map do |k,v| 28 | if v.nil? 29 | nil # strip out non-values 30 | else 31 | if v.is_a?( Array ) 32 | v = "From #{v.shift} to #{v.shift}" 33 | end 34 | 35 | "#{k}: #{v}" 36 | end 37 | end.compact.join('
') 38 | end 39 | 40 | def sort_audits_by_date( collection ) 41 | collection.sort_by(&:created_at).reverse 42 | end 43 | 44 | def audit_user( audit ) 45 | if audit.user.is_a?( User ) 46 | audit.user.login 47 | else 48 | audit.username || 'UNKNOWN' 49 | end 50 | end 51 | 52 | end 53 | -------------------------------------------------------------------------------- /app/views/macro_steps/_macro_step.html.haml: -------------------------------------------------------------------------------- 1 | %tr[ macro_step, :marker ] 2 | %tr[ macro_step, :show ] 3 | %td{ :width => 50 }= macro_step.position 4 | %td{ :width => 90 }= macro_step.action 5 | %td{ :width => 80 }= macro_step.record_type 6 | %td{ :width => 230 }= macro_step.name 7 | %td{ :width => 30 }= macro_step.prio 8 | %td{ :width => 230 }= macro_step.content 9 | %td{ :width => 40 } 10 | = link_to_function info_icon('table_edit.png', 'edit-step'), "editRecordTemplate(#{macro_step.id})" 11 | = link_to info_icon('table_delete.png', 'delete-step'), macro_macro_step_path(@macro, macro_step), :method => :delete, :confirm => t(:confirm_macro_step_delte) 12 | %tr[ macro_step, :edit ]{ :style => 'display: none' } 13 | %td{ :colspan => 7 } 14 | = form_for( [@macro, macro_step], :remote => true ) do |f| 15 | %div[ macro_step, :error ] 16 | %table.gridwide 17 | %tr 18 | %td{ :width => 50 }= f.text_field :position, :size => 3 19 | %td{ :width => 90 }= f.select :action, MacroStep.valid_actions.map { |a| [ a.humanize, a ] } 20 | %td{ :width => 80 }= f.select :record_type, Macro.record_types.map { |t| [t,t] } 21 | %td{ :width => 220 }= f.text_field :name, :size => 15 22 | %td{ :width => 30 } 23 | - if macro_step.record_type == 'MX' 24 | = f.text_field :prio, :size => 2 25 | %td{ :width => 220 }= f.text_field :content, :size => 15 26 | %td{ :width => 44 } 27 | = image_submit_tag "table_save.png", { :class => 'nb' } 28 | = link_to_function image_tag('cancel.png'), "hideRecordTemplate(#{macro_step.id})" 29 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | # Methods added to this helper will be available to all templates in the application. 2 | module ApplicationHelper 3 | 4 | # Outputs a page title with +@page_title+ appended 5 | def page_title 6 | title = t(:layout_main_title) 7 | title << ' - ' + @page_title unless @page_title.nil? 8 | title 9 | end 10 | 11 | # Output the flashes if they exist 12 | def show_flash 13 | html = '' 14 | [ :alert, :notice, :info, :warning, :error ].each do |f| 15 | options = { :id => "flash-#{f}", :class => "flash-#{f}" } 16 | options.merge!( :style => 'display:none' ) if flash[f].nil? 17 | html << content_tag( 'div', options ) { flash[f] || '' } 18 | end 19 | html.html_safe 20 | end 21 | 22 | # Link to Zytrax 23 | def dns_book( text, link ) 24 | link_to text, "http://www.zytrax.com/books/dns/#{link}", :target => '_blank' 25 | end 26 | 27 | # Add a cancel link for shared forms. Looks at the provided object and either 28 | # creates a link to the index or show actions. 29 | def link_to_cancel( object ) 30 | path = object.class.name.tableize 31 | path = if object.new_record? 32 | send( path.pluralize + '_path' ) 33 | else 34 | send( path.singularize + '_path', object ) 35 | end 36 | link_to "Cancel", path 37 | end 38 | 39 | def help_icon( dom_id ) 40 | image_tag('help.png', :id => "help-icn-#{dom_id}", :class => 'help-icn', "data-help" => dom_id ) 41 | end 42 | 43 | def info_icon( image, dom_id ) 44 | image_tag( image , :id => "help-icn-#{dom_id}", :class => 'help-icn', "data-help" => dom_id ) 45 | end 46 | 47 | end 48 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | PowerdnsOnRails::Application.configure do 2 | # Settings specified here will take precedence over those in config/environment.rb 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the webserver when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Log error messages when you accidentally call methods on nil. 10 | config.whiny_nils = true 11 | 12 | # Show full error reports and disable caching 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = false 15 | 16 | # Don't care if the mailer can't send 17 | config.action_mailer.raise_delivery_errors = false 18 | 19 | # Print deprecation notices to the Rails logger 20 | config.active_support.deprecation = :log 21 | 22 | # Only use best-standards-support built into browsers 23 | config.action_dispatch.best_standards_support = :builtin 24 | 25 | # Localhost default for devise 26 | config.action_mailer.default_url_options = { :host => 'localhost' } 27 | 28 | # Do not compress assets 29 | config.assets.compress = false 30 | 31 | # Expands the lines which load the assets 32 | config.assets.debug = true 33 | 34 | # Raise exception on mass assignment protection for Active Record models 35 | config.active_record.mass_assignment_sanitizer = :strict 36 | 37 | # Log the query plan for queries taking more than this (works 38 | # with SQLite, MySQL, and PostgreSQL) 39 | config.active_record.auto_explain_threshold_in_seconds = 0.5 40 | end 41 | 42 | -------------------------------------------------------------------------------- /spec/factories/domain_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | 3 | factory :domain, :class => 'Domain' do 4 | name 'example.com' 5 | add_attribute :type, 'NATIVE' 6 | ttl 86400 7 | # soa 8 | primary_ns { |d| "ns1.#{d.name}" } 9 | contact { |d| "admin@#{d.name}" } 10 | refresh 10800 11 | self.retry 7200 # retry is a keyword in ruby 12 | expire 604800 13 | minimum 10800 14 | end 15 | 16 | #factory :soa, :class => 'SOA' do 17 | # name { |r| r.domain.name } 18 | # ttl 86400 19 | # #content { |r| "ns1.#{r.domain.name} admin@#{r.domain.name} 2008040101 10800 7200 604800 10800" } 20 | # primary_ns { |r| "ns1.#{r.domain.name}" } 21 | # contact { |r| "admin@#{r.domain.name}" } 22 | # refresh 10700 23 | # retry 7200 24 | # expire 604800 25 | # minimum 10800 26 | #end 27 | 28 | factory :ns, :class => NS do 29 | ttl 86400 30 | name { |r| r.domain.name } 31 | content { |r| "ns1.#{r.domain.name}" } 32 | end 33 | 34 | factory :ns_a, :class => A do 35 | ttl 86400 36 | name { |r| "ns1.#{r.domain.name}" } 37 | content "10.0.0.1" 38 | end 39 | 40 | factory :a, :class => A do 41 | ttl 86400 42 | name { |r| r.domain.name } 43 | content '10.0.0.3' 44 | end 45 | 46 | factory :www, :class => A do 47 | ttl 86400 48 | name { |r| "www.#{r.domain.name}" } 49 | content '10.0.0.3' 50 | end 51 | 52 | factory :mx, :class => MX do 53 | ttl 86400 54 | name { |r| r.domain.name } 55 | content { |r| "mail.#{r.domain.name}" } 56 | prio 10 57 | end 58 | 59 | factory :mx_a, :class => A do 60 | ttl 86400 61 | name { |r| "mail.#{r.domain.name}" } 62 | content '10.0.0.4' 63 | end 64 | 65 | end 66 | -------------------------------------------------------------------------------- /spec/views/templates/show.html.haml_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "templates/show.html.haml" do 4 | context "for complete templates" do 5 | before(:each) do 6 | @zone_template = FactoryGirl.create(:zone_template) 7 | FactoryGirl.create(:template_soa, :zone_template => @zone_template) 8 | 9 | assign(:zone_template, @zone_template) 10 | assign(:record_template, RecordTemplate.new( :record_type => 'A' )) 11 | 12 | render 13 | end 14 | 15 | it "should have the template name" do 16 | rendered.should have_tag('h1', :content => @zone_template.name) 17 | end 18 | 19 | it "should have a table with template overview" do 20 | rendered.should have_selector('table.grid td', :content => 'Name') 21 | rendered.should have_selector('table.grid td', :content => 'TTL') 22 | end 23 | 24 | it "should have the record templates" do 25 | rendered.should have_selector('h1', :content => 'Record templates') 26 | rendered.should have_selector('table#record-table') 27 | end 28 | 29 | it "should not have an SOA warning" do 30 | violated "ZoneTemplate does not have SOA" unless @zone_template.has_soa? 31 | 32 | rendered.should_not have_selector('div#soa-warning') 33 | end 34 | end 35 | 36 | 37 | context "for partial templates" do 38 | before(:each) do 39 | @zone_template = FactoryGirl.create(:zone_template) 40 | assign(:zone_template, @zone_template) 41 | assign(:record_template, RecordTemplate.new( :record_type => 'A' )) 42 | 43 | render 44 | end 45 | 46 | it "should have an SOA warning" do 47 | rendered.should have_tag('div#soa-warning') 48 | end 49 | 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /app/controllers/macro_steps_controller.rb: -------------------------------------------------------------------------------- 1 | class MacroStepsController < InheritedResources::Base 2 | 3 | belongs_to :macro 4 | respond_to :xml, :json, :js 5 | 6 | protected 7 | 8 | def parent 9 | Macro.user( current_user ).find( params[:macro_id] ) 10 | end 11 | 12 | def collection 13 | parent.macro_steps 14 | end 15 | 16 | def resource 17 | collection.find( params[:id] ) 18 | end 19 | 20 | public 21 | 22 | def create 23 | # Check for any previous macro steps 24 | if parent.macro_steps.any? 25 | # Check for the parameter 26 | unless params[:macro_step][:position].blank? 27 | position = params[:macro_step].delete(:position) 28 | else 29 | position = parent.macro_steps.last.position + 1 30 | end 31 | else 32 | position = '1' 33 | end 34 | 35 | @macro_step = parent.macro_steps.create( params[:macro_step] ) 36 | 37 | @macro_step.insert_at( position.to_i ) if position && !@macro_step.new_record? 38 | 39 | parent.save 40 | @macro = parent 41 | 42 | respond_to do |format| 43 | format.js 44 | end 45 | end 46 | 47 | def update 48 | position = params[:macro_step].delete(:position) 49 | 50 | @macro_step = parent.macro_steps.find( params[:id] ) 51 | @macro_step.update_attributes( params[:macro_step] ) 52 | 53 | @macro_step.insert_at( position.to_i ) if position 54 | 55 | @macro = parent 56 | 57 | respond_to do |wants| 58 | wants.js 59 | end 60 | end 61 | 62 | def destroy 63 | @macro_step = parent.macro_steps.find( params[:id] ) 64 | @macro_step.destroy 65 | 66 | flash[:info] = t :message_macro_step_removed 67 | redirect_to macro_path( parent ) 68 | end 69 | 70 | end 71 | -------------------------------------------------------------------------------- /spec/views/audits/domain.html.haml_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "audits/domain.html.haml" do 4 | context "and domain audits" do 5 | 6 | before(:each) do 7 | @domain = FactoryGirl.create(:domain) 8 | end 9 | 10 | it "should handle no audit entries on the domain" do 11 | @domain.expects(:audits).returns( [] ) 12 | assign(:domain, @domain) 13 | 14 | render 15 | 16 | rendered.should have_tag("em", :content => "No revisions found for the domain") 17 | end 18 | 19 | it "should handle audit entries on the domain" do 20 | audit = Audit.new( 21 | :auditable => @domain, 22 | :created_at => Time.now, 23 | :version => 1, 24 | :audited_changes => {}, 25 | :action => 'create', 26 | :username => 'admin' 27 | ) 28 | @domain.expects(:audits).at_most(2).returns( [ audit ] ) 29 | 30 | assign(:domain, @domain) 31 | render 32 | 33 | rendered.should have_tag("ul > li > a", :content => "1 create by") 34 | end 35 | 36 | end 37 | 38 | context "and resource record audits" do 39 | 40 | before(:each) do 41 | Audit.as_user( 'admin' ) do 42 | @domain = FactoryGirl.create(:domain) 43 | end 44 | end 45 | 46 | it "should handle no audit entries" do 47 | @domain.expects(:associated_audits).at_most(2).returns( [] ) 48 | assign(:domain, @domain) 49 | 50 | render 51 | 52 | rendered.should have_tag("em", :content => "No revisions found for any resource records of the domain") 53 | end 54 | 55 | it "should handle audit entries" do 56 | assign(:domain, @domain) 57 | 58 | render 59 | 60 | rendered.should have_tag("ul > li > a", :content => "1 create by admin") 61 | end 62 | 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /db/migrate/20101118071851_convert_roles_to_columns_on_user.rb: -------------------------------------------------------------------------------- 1 | class ConvertRolesToColumnsOnUser < ActiveRecord::Migration 2 | class ::Role < ActiveRecord::Base 3 | has_and_belongs_to_many :users 4 | end 5 | 6 | def self.up 7 | change_table :users do |t| 8 | t.boolean :admin, :default => false 9 | t.boolean :auth_tokens, :default => false 10 | end 11 | 12 | User.reset_column_information 13 | 14 | User.send( :has_and_belongs_to_many, :roles ) 15 | 16 | User.all.each do |user| 17 | user.roles.each do |role| 18 | p [ :role, user.login, role.name ] 19 | user[:admin] = true if role.name == 'admin' 20 | user[:auth_tokens] = true if role.name == 'auth_token' 21 | user.save 22 | end 23 | end 24 | 25 | drop_table :roles 26 | drop_table :roles_users 27 | end 28 | 29 | def self.down 30 | create_table "roles", :force => true do |t| 31 | t.string "name" 32 | end 33 | 34 | create_table "roles_users", :id => false, :force => true do |t| 35 | t.integer "role_id" 36 | t.integer "user_id" 37 | end 38 | 39 | add_index "roles_users", ["role_id"], :name => "index_roles_users_on_role_id" 40 | add_index "roles_users", ["user_id"], :name => "index_roles_users_on_user_id" 41 | 42 | User.send( :has_and_belongs_to_many, :roles ) 43 | 44 | admin = Role.create!( :name => 'admin' ) 45 | owner = Role.create!( :name => 'owner' ) 46 | token = Role.create!( :name => 'auth_token') 47 | 48 | User.all.each do |user| 49 | user.roles << admin if user.admin? 50 | user.roles << token if user.auth_tokens? 51 | user.roles << owner if !user.admin? && !user.auth_tokens? 52 | user.save 53 | end 54 | 55 | change_table :users do |t| 56 | t.drop :admin, :auth_tokens 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title= page_title 5 | = stylesheet_link_tag 'application', 'tipTip', 'bold-dark' 6 | = javascript_include_tag :defaults, 'jquery.tipTip', 'humane' 7 | = csrf_meta_tag 8 | %body 9 | #Container 10 | #Header 11 | %h1 12 | =t :layout_main_title 13 | - if current_user 14 | #search-bar{:style => "display: inline; float: right;"} 15 | = form_tag( { :controller => :search, :action => :results }, { :method => :get } ) do 16 | = text_field_tag :q 17 | = submit_tag( t(:generic_search) ) 18 | #Nav 19 | %ul 20 | %li= link_to((t :layout_menu_home), root_url) 21 | %li= link_to((t :layout_menu_domains), domains_path) 22 | %li= link_to((t :layout_menu_templates), zone_templates_path) 23 | %li= link_to((t :layout_menu_macros), macros_path) 24 | - if current_user.admin? 25 | %li= link_to((t :layout_menu_logs), audits_path) 26 | %li= link_to((t :layout_menu_users), users_path) 27 | %li= link_to((t :layout_menu_reports), reports_path) 28 | %li= link_to((t :layout_menu_logout), destroy_user_session_path) 29 | 30 | #Body 31 | = show_flash 32 | 33 | = yield 34 | #Footer 35 | = link_to "PowerDNS on Rails", "http://www.opensourcery.co.za/powerdns-on-rails", :target => '_blank' 36 | \- © Kenneth Kalmer 37 | %br 38 | Released under the MIT License. 39 | %br 40 | %br 41 | = link_to "Report Bugs", "http://kennethkalmer.lighthouseapp.com/projects/11831-powerdns-on-rails", :target => '_blank' 42 | \- 43 | = link_to "Github", "http://github.com/kennethkalmer/powerdns-on-rails", :target => '_blank' 44 | 45 | -------------------------------------------------------------------------------- /db/migrate/20110306115116_devise_create_users.rb: -------------------------------------------------------------------------------- 1 | class DeviseCreateUsers < ActiveRecord::Migration 2 | def self.up 3 | # encrypting passwords and authentication related fields 4 | rename_column :users, "crypted_password", "encrypted_password" 5 | change_column :users, "encrypted_password", :string, :limit => 128, :default => "", :null => false 6 | rename_column :users, "salt", "password_salt" 7 | change_column :users, "password_salt", :string, :default => "", :null => false 8 | 9 | # confirmation related fields 10 | rename_column :users, "activation_code", "confirmation_token" 11 | rename_column :users, "activated_at", "confirmed_at" 12 | change_column :users, "confirmation_token", :string 13 | add_column :users, "confirmation_sent_at", :datetime 14 | 15 | # reset password related fields 16 | add_column :users, "reset_password_token", :string 17 | 18 | # rememberme related fields 19 | add_column :users, "remember_created_at", :datetime #additional field required for devise. 20 | 21 | # Current users can carry on logging in 22 | User.all.each(&:confirm!) 23 | end 24 | 25 | def self.down 26 | # rememberme related fields 27 | remove_column :users, "remember_created_at" 28 | 29 | # reset password related fields 30 | remove_column :users, "reset_password_token" 31 | 32 | # confirmation related fields 33 | rename_column :users, "confirmation_token", "activation_code" 34 | rename_column :users, "confirmed_at", "activated_at" 35 | change_column :users, "activation_code", :string 36 | remove_column :users, "confirmation_sent_at" 37 | 38 | # encrypting passwords and authentication related fields 39 | rename_column :users, "encrypted_password", "crypted_password" 40 | change_column :users, "crypted_password", :string, :limit => 40 41 | rename_column :users, "password_salt", "salt" 42 | change_column :users, "salt", :string, :limit => 40 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /app/models/macro_step.rb: -------------------------------------------------------------------------------- 1 | class MacroStep < ActiveRecord::Base 2 | 3 | @@valid_actions = %w{ create update remove create_update } 4 | cattr_reader :valid_actions 5 | 6 | validates_presence_of :macro_id 7 | validates_presence_of :content, :if => :content_required? 8 | validates_inclusion_of :action, :in => self.valid_actions 9 | validates_inclusion_of :record_type, :in => Macro.record_types 10 | validate :validate_macro_step 11 | 12 | belongs_to :macro 13 | 14 | acts_as_list :scope => :macro 15 | 16 | # Convert this step into a valid #Record 17 | def build( domain = nil ) 18 | record_class = self.record_type.constantize 19 | 20 | # make a clean copy of ourself 21 | attrs = self.attributes.dup 22 | attrs.delete_if { |k,_| !record_class.columns.map(&:name).include?( k ) } 23 | attrs.delete('id') 24 | 25 | # parse each attribute for %ZONE% 26 | unless domain.nil? 27 | attrs.keys.each do |k| 28 | attrs[k] = attrs[k].gsub( '%ZONE%', domain.name ) if attrs[k].is_a?( String ) 29 | end 30 | end 31 | 32 | record_class.new( attrs ) 33 | end 34 | 35 | # Here we perform some magic to inherit the validations from our parent 36 | # #Record (record_type) 37 | def validate_macro_step 38 | return if self.record_type.blank? || !self.errors[:record_type].blank? 39 | 40 | record = build 41 | 42 | record.errors.each do |k,v| 43 | next if k == :domain_id || k == :ttl 44 | next if k == :content || k == :prio unless content_required? 45 | 46 | # Since we don't have a domain, blank name validations will 47 | # prevent pretty useful (and dangerous) macro steps from being 48 | # created. This check offsets that, but might be flawed. 49 | next if k == :name && v == I18n.t('activerecord.errors.messages.blank') 50 | 51 | self.errors.add( k, v ) 52 | end unless record.valid? 53 | end 54 | 55 | def content_required? 56 | self.action != 'remove' 57 | end 58 | 59 | end 60 | -------------------------------------------------------------------------------- /spec/controllers/reports_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ReportsController, "index" do 4 | before(:each) do 5 | sign_in(FactoryGirl.create(:admin)) 6 | 7 | FactoryGirl.create(:domain) 8 | q = FactoryGirl.create(:quentin) 9 | FactoryGirl.create(:domain, :name => 'example.net', :user => q) 10 | end 11 | 12 | it "should display all users to the admin" do 13 | get 'index' 14 | 15 | response.should render_template('reports/index') 16 | assigns(:users).should_not be_empty 17 | assigns(:users).size.should be(1) 18 | end 19 | 20 | it "should display total system domains and total domains to the admin" do 21 | get 'index' 22 | 23 | response.should render_template('reports/index') 24 | assigns(:total_domains).should be(Domain.count) 25 | assigns(:system_domains).should be(1) 26 | end 27 | end 28 | 29 | describe ReportsController, "results" do 30 | before(:each) do 31 | sign_in(FactoryGirl.create(:admin)) 32 | end 33 | 34 | it "should display a list of users for a search hit" do 35 | FactoryGirl.create(:aaron) 36 | FactoryGirl.create(:api_client) 37 | 38 | get 'results', :q => "a" 39 | 40 | response.should render_template('reports/results') 41 | assigns(:results).should_not be_empty 42 | assigns(:results).size.should be(3) 43 | end 44 | 45 | it "should redirect to reports/index if the search query is empty" do 46 | get 'results' , :q => "" 47 | 48 | response.should be_redirect 49 | response.should redirect_to( reports_path ) 50 | end 51 | 52 | end 53 | 54 | describe ReportsController , "view" do 55 | before(:each) do 56 | sign_in(FactoryGirl.create(:admin)) 57 | end 58 | 59 | it "should show a user reports" do 60 | get "view" , :id => FactoryGirl.create(:aaron).id 61 | 62 | response.should render_template("reports/view") 63 | assigns(:user).should_not be_nil 64 | assigns(:user).login.should == 'aaron' 65 | end 66 | 67 | end 68 | 69 | -------------------------------------------------------------------------------- /public/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // Place your application-specific JavaScript functions and classes here 2 | // This file is automatically included by javascript_include_tag :defaults 3 | 4 | $(document).ready(function() { 5 | // AJAX activity indicator 6 | $('body').append('
Processing
'); 7 | 8 | // Setup tooltips where required 9 | $('.help-icn').each(function(i, icon){ 10 | $(icon).tipTip({ 11 | content: $( "#" + $(icon).data("help") ).text() 12 | }); 13 | }); 14 | 15 | // Used by the new record form 16 | $('#record-form #record_type').change(function() { 17 | toggleRecordFields( $(this).val() ); 18 | }); 19 | 20 | // Used by the new domain form 21 | $('#domain_type').change(function() { 22 | if ( $(this).val() == 'SLAVE' ) { 23 | $('#master-address').show(); 24 | $('#zone-templates').hide(); 25 | $('#no-template-input').hide(); 26 | } else { 27 | $('#master-address').hide(); 28 | $('#zone-templates').show(); 29 | $('#no-template-input').show(); 30 | } 31 | }); 32 | 33 | // Used by the new domain form 34 | $('#domain_zone_template_id').change(function() { 35 | if ( $(this).val() == '' ) { 36 | $('#no-template-input').show(); 37 | $('#domain-type').show(); 38 | } else { 39 | $('#no-template-input').hide(); 40 | $('#domain-type').hide(); 41 | } 42 | }); 43 | 44 | // Used by the new record template form 45 | $('#record-form #record_template_record_type').change(function() { 46 | toggleRecordFields( $(this).val() ); 47 | }); 48 | 49 | // Used by the new macro step form 50 | $('#record-form #macro_step_record_type').change(function() { 51 | toggleRecordFields( $(this).val() ); 52 | }); 53 | }); 54 | 55 | // Ajax activity indicator bound to ajax start/stop document events 56 | $(document).ajaxStart(function(){ 57 | $('#ajaxBusy').show(); 58 | }).ajaxStop(function(){ 59 | $('#ajaxBusy').hide(); 60 | }); 61 | -------------------------------------------------------------------------------- /app/views/domains/_record.html.haml: -------------------------------------------------------------------------------- 1 | - css_class = cycle('even', 'odd') 2 | 3 | %tr[ record, :marker ] 4 | %tr[ record, :show ]{:class => css_class} 5 | %td{ :width => 250 }= record.shortname 6 | %td{ :width => 50 }= record.ttl 7 | %td{ :width => 30 }= record.type 8 | %td{ :width => 30 }= record.prio 9 | %td{ :width => 250 }= record.content 10 | %td{ :width => 39 } 11 | - unless record.domain.slave? 12 | - if current_user || current_token.can_change?( record ) 13 | = link_to_function info_icon('database_edit.png', "record-template-edit"), "editRecord(#{record.id}, '#{record.type.downcase}')" 14 | - if current_user || current_token.can_remove?( record ) 15 | = link_to info_icon('database_delete.png', "record-template-delete"), domain_record_path( record.domain, record ), :method => :delete, :confirm => t(:confirm_domain_delte_record) 16 | 17 | - unless record.domain.slave? 18 | - if current_user || current_token.can_change?( record ) 19 | %tr[ record, :edit ]{ :style => "display: none;", :class => css_class} 20 | %td{ :colspan => 7 } 21 | = form_for( record, :as => :record, :url => domain_record_path(record.domain, record ), :remote => true ) do |f| 22 | %div[ record, :error ] 23 | %table.gridwide 24 | %tr 25 | %td{ :width => 250 }= f.text_field :shortname, :size => 15 26 | %td{ :width => 50 }= f.text_field :ttl, :size => 3 27 | %td{ :width => 30 }= record.type 28 | %td{ :width => 30 } 29 | - if record.supports_prio? 30 | = f.text_field :prio, :size => 2 31 | - else 32 |   33 | %td{ :width => 250 }= f.text_field :content, :size => 15 34 | %td{ :width => 39 } 35 | = image_submit_tag "table_save.png", { :class => 'nb' } 36 | = link_to_function image_tag("cancel.png"), "hideRecord(#{record.id}, '#{record.type.downcase}')" 37 | -------------------------------------------------------------------------------- /config/locales/devise.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | errors: 3 | messages: 4 | not_found: "not found" 5 | already_confirmed: "was already confirmed" 6 | not_locked: "was not locked" 7 | 8 | devise: 9 | failure: 10 | unauthenticated: 'You need to sign in or sign up before continuing.' 11 | unconfirmed: 'You have to confirm your account before continuing.' 12 | locked: 'Your account is locked.' 13 | invalid: 'Invalid email or password.' 14 | invalid_token: 'Invalid authentication token.' 15 | timeout: 'Your session expired, please sign in again to continue.' 16 | inactive: 'Your account was not activated yet.' 17 | sessions: 18 | signed_in: 'Signed in successfully.' 19 | signed_out: 'Signed out successfully.' 20 | passwords: 21 | send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.' 22 | updated: 'Your password was changed successfully. You are now signed in.' 23 | confirmations: 24 | send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.' 25 | confirmed: 'Your account was successfully confirmed. You are now signed in.' 26 | registrations: 27 | signed_up: 'You have signed up successfully. If enabled, a confirmation was sent to your e-mail.' 28 | updated: 'You updated your account successfully.' 29 | destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.' 30 | unlocks: 31 | send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.' 32 | unlocked: 'Your account was successfully unlocked. You are now signed in.' 33 | mailer: 34 | confirmation_instructions: 35 | subject: 'Confirmation instructions' 36 | reset_password_instructions: 37 | subject: 'Reset password instructions' 38 | unlock_instructions: 39 | subject: 'Unlock Instructions' 40 | -------------------------------------------------------------------------------- /spec/controllers/templates_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe TemplatesController, "and admins" do 4 | before(:each) do 5 | sign_in(FactoryGirl.create(:admin)) 6 | end 7 | 8 | it "should have a template list" do 9 | FactoryGirl.create(:zone_template) 10 | 11 | get :index 12 | 13 | assigns(:zone_templates).should_not be_empty 14 | assigns(:zone_templates).size.should be( ZoneTemplate.count ) 15 | end 16 | 17 | it "should have a detailed view of a template" do 18 | get :show, :id => FactoryGirl.create(:zone_template).id 19 | 20 | assigns(:zone_template).should_not be_nil 21 | 22 | response.should render_template('templates/show') 23 | end 24 | 25 | it "should redirect to the template on create" do 26 | expect { 27 | post :create, :zone_template => { :name => 'Foo' } 28 | }.to change( ZoneTemplate, :count ).by(1) 29 | 30 | response.should redirect_to( zone_template_path( assigns(:zone_template) ) ) 31 | end 32 | 33 | end 34 | 35 | describe TemplatesController, "and users" do 36 | before(:each) do 37 | @quentin = FactoryGirl.create(:quentin) 38 | sign_in(@quentin) 39 | end 40 | 41 | it "should have a limited list" do 42 | FactoryGirl.create(:zone_template, :user => @quentin) 43 | FactoryGirl.create(:zone_template, :name => '!Quentin') 44 | 45 | get :index 46 | 47 | assigns(:zone_templates).should_not be_empty 48 | assigns(:zone_templates).size.should be(1) 49 | end 50 | 51 | it "should not have a list of users when showing the new form" do 52 | get :new 53 | 54 | assigns(:users).should be_nil 55 | end 56 | end 57 | 58 | describe TemplatesController, "should handle a REST client" do 59 | before(:each) do 60 | sign_in(FactoryGirl.create(:api_client)) 61 | end 62 | 63 | it "asking for a list of templates" do 64 | FactoryGirl.create(:zone_template) 65 | 66 | get :index, :format => "xml" 67 | 68 | response.should have_tag('zone-templates > zone-template') 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /lib/scoped_finders.rb: -------------------------------------------------------------------------------- 1 | module ScopedFinders 2 | 3 | def self.included( base ) #:nodoc: 4 | base.extend( ClassMethods ) 5 | end 6 | 7 | module ClassMethods 8 | 9 | def scope_user 10 | 11 | extend SingletonMethods 12 | 13 | class << self 14 | alias_method_chain :find, :scope 15 | alias_method_chain :paginate, :scope 16 | end 17 | 18 | end 19 | 20 | end 21 | 22 | module SingletonMethods 23 | # Convenient scoped finder method that restricts lookups to the specified 24 | # :user. If the user has an admin role, the scoping is discarded totally, 25 | # since an admin _is a admin_. 26 | # 27 | # Example: 28 | # 29 | # Domain.find(:all) # Normal behavior 30 | # Domain.find(:all, :user => user_instance) # Will scope lookups to the user 31 | # 32 | def find_with_scope( *args ) 33 | options = args.extract_options! 34 | user = options.delete( :user ) 35 | 36 | unless user.nil? || user.has_role?( 'admin' ) 37 | with_scope( :find => { :conditions => [ 'user_id = ?', user.id ] } ) do 38 | find_without_scope( *args << options ) 39 | end 40 | else 41 | find_without_scope( *args << options ) 42 | end 43 | end 44 | 45 | # Paginated find with scope. See #find. 46 | def paginate_with_scope( *args, &block ) 47 | options = args.pop 48 | user = options.delete( :user ) 49 | 50 | unless user.nil? || user.has_role?( 'admin' ) 51 | with_scope( :find => { :conditions => [ 'user_id = ?', user.id ] } ) do 52 | paginate_without_scope( *args << options, &block ) 53 | end 54 | else 55 | paginate_without_scope( *args << options, &block ) 56 | end 57 | end 58 | 59 | # For our lookup purposes 60 | def search( params, page, user = nil ) 61 | paginate :per_page => 5, :page => page, 62 | :conditions => ['name LIKE ?', "%#{params.chomp}%"], 63 | :user => user 64 | end 65 | end 66 | 67 | end 68 | 69 | ActiveRecord::Base.send( :include, ScopedFinders ) 70 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | PowerdnsOnRails::Application.configure do 2 | # Settings specified here will take precedence over those in config/environment.rb 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Log error messages when you accidentally call methods on nil. 11 | config.whiny_nils = true 12 | 13 | # Show full error reports and disable caching 14 | config.consider_all_requests_local = true 15 | config.action_controller.perform_caching = false 16 | 17 | # Raise exceptions instead of rendering exception templates 18 | config.action_dispatch.show_exceptions = false 19 | 20 | # Disable request forgery protection in test environment 21 | config.action_controller.allow_forgery_protection = false 22 | 23 | # Tell Action Mailer not to deliver emails to the real world. 24 | # The :test delivery method accumulates sent emails in the 25 | # ActionMailer::Base.deliveries array. 26 | config.action_mailer.delivery_method = :test 27 | 28 | # Use SQL instead of Active Record's schema dumper when creating the test database. 29 | # This is necessary if your schema can't be completely dumped by the schema dumper, 30 | # like if you have constraints or database-specific column types 31 | # config.active_record.schema_format = :sql 32 | 33 | # Print deprecation notices to the stderr 34 | config.active_support.deprecation = :stderr 35 | 36 | # Default host for testing mail 37 | config.action_mailer.default_url_options = { :host => "example.com" } 38 | 39 | # Allow pass debug_assets=true as a query parameter to load pages with unpackaged assets 40 | config.assets.allow_debugging = true 41 | 42 | # Configure static asset server for tests with Cache-Control for performance 43 | config.serve_static_assets = true 44 | config.static_cache_control = "public, max-age=3600" 45 | end 46 | -------------------------------------------------------------------------------- /app/views/domains/_soa_record.html.haml: -------------------------------------------------------------------------------- 1 | #soa 2 | %table#soa-view.grid 3 | %tr 4 | %td= t :label_domain_primary_name_server 5 | %td= soa_record.primary_ns 6 | %tr 7 | %td= t :label_domain_contact 8 | %td= soa_record.contact 9 | %tr 10 | %td= t :label_domain_refresh 11 | %td= soa_record.refresh 12 | %tr 13 | %td= t :label_domain_retry 14 | %td= soa_record.retry 15 | %tr 16 | %td= t :label_domain_expire 17 | %td= soa_record.expire 18 | %tr 19 | %td= t :label_domain_minimum 20 | %td= soa_record.minimum 21 | 22 | - if current_user 23 | #soa-form{ :style => "display: none;" } 24 | = form_for( @domain.soa_record, :url => update_soa_domain_record_path( @domain.soa_record, :domain_id => @domain.id ), :remote => true ) do |f| 25 | = error_messages_for @domain.soa_record 26 | %table.grid 27 | %tr 28 | %td= t :label_domain_primary_name_server 29 | %td= f.text_field :primary_ns 30 | %td= help_icon('help-primary-ns') 31 | %tr 32 | %td= t :label_domain_contact 33 | %td= f.text_field :contact 34 | %td= help_icon('help-contact') 35 | %tr 36 | %td= t :label_domain_refresh 37 | %td 38 | = f.text_field :refresh, :size => 6 39 | = help_icon('help-refresh') 40 | %td   41 | %tr 42 | %td= t :label_domain_retry 43 | %td 44 | = f.text_field :retry, :size => 6 45 | = help_icon('help-retry') 46 | %td   47 | %tr 48 | %td= t :label_domain_expire 49 | %td 50 | = f.text_field :expire, :size => 6 51 | = help_icon('help-expire') 52 | %td   53 | %tr 54 | %td= t :label_domain_minimum 55 | %td 56 | = f.text_field :minimum, :size => 6 57 | = help_icon('help-minimum') 58 | %td   59 | %tr 60 | %td.right= link_to_function t(:generic_cancel), "showSOAForm()" 61 | %td= submit_tag t(:generic_update) 62 | %td   63 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | # If you have a Gemfile, require the gems listed there, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(:default, Rails.env) if defined?(Bundler) 8 | 9 | module PowerdnsOnRails 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | 15 | # Custom directories with classes and modules you want to be autoloadable. 16 | # config.autoload_paths += %W(#{config.root}/extras) 17 | config.autoload_paths += %W( #{config.root}/lib ) 18 | 19 | # Only load the plugins named here, in the order given (default is alphabetical). 20 | # :all can be used as a placeholder for all plugins not explicitly named. 21 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] 22 | 23 | # Activate observers that should always be running. 24 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer 25 | 26 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 27 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 28 | # config.time_zone = 'Central Time (US & Canada)' 29 | 30 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 31 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 32 | # config.i18n.default_locale = :de 33 | 34 | # JavaScript files you want as :defaults (application.js is always included). 35 | #config.action_view.javascript_expansions[:defaults] = %w(jquery rails application) 36 | 37 | # Configure the default encoding used in templates for Ruby 1.9. 38 | config.encoding = "utf-8" 39 | 40 | # Configure sensitive parameters which will be filtered from the log file. 41 | config.filter_parameters += [:password] 42 | 43 | config.active_record.whitelist_attributes = false 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /spec/controllers/search_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe SearchController, "for admins" do 4 | 5 | before(:each) do 6 | #session[:user_id] = FactoryGirl.create(:admin).id 7 | sign_in FactoryGirl.create(:admin) 8 | 9 | FactoryGirl.create(:domain, :name => 'example.com') 10 | FactoryGirl.create(:domain, :name => 'example.net') 11 | end 12 | 13 | it "should return results when searched legally" do 14 | get :results, :q => 'exa' 15 | 16 | assigns(:results).should_not be_nil 17 | response.should render_template('search/results') 18 | end 19 | 20 | it "should handle whitespace in the query" do 21 | get :results, :q => ' exa ' 22 | 23 | assigns(:results).should_not be_nil 24 | response.should render_template('results') 25 | end 26 | 27 | it "should redirect to the index page when nothing has been searched for" do 28 | get :results, :q => '' 29 | 30 | response.should be_redirect 31 | response.should redirect_to( root_path ) 32 | end 33 | 34 | it "should redirect to the domain page if only one result is found" do 35 | domain = FactoryGirl.create(:domain, :name => 'slave-example.com') 36 | 37 | get :results, :q => 'slave-example.com' 38 | 39 | response.should be_redirect 40 | response.should redirect_to( domain_path( domain ) ) 41 | end 42 | 43 | end 44 | 45 | describe SearchController, "for api clients" do 46 | before(:each) do 47 | sign_in(FactoryGirl.create(:api_client)) 48 | 49 | FactoryGirl.create(:domain, :name => 'example.com') 50 | FactoryGirl.create(:domain, :name => 'example.net') 51 | end 52 | 53 | it "should return an empty JSON response for no results" do 54 | get :results, :q => 'amazon', :format => 'json' 55 | 56 | assigns(:results).should be_empty 57 | 58 | response.body.should == "[]" 59 | end 60 | 61 | it "should return a JSON set of results" do 62 | get :results, :q => 'example', :format => 'json' 63 | 64 | assigns(:results).should_not be_empty 65 | 66 | json = ActiveSupport::JSON.decode( response.body ) 67 | json.size.should be(2) 68 | json.first["domain"].keys.should include('id', 'name') 69 | json.first["domain"]["name"].should match(/example/) 70 | json.first["domain"]["id"].to_s.should match(/\d+/) 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /CONVENTIONS.textile: -------------------------------------------------------------------------------- 1 | h1. Conventions 2 | 3 | Just like its framework, PowerDNS on Rails follows a strict convention over configuration approach. Based on our experience, and scouring the internet, we've made certain assumptions that might, or might not, fit into how you run your own DNS servers. As the projet progresses (or demand increases) these will become more configurable with time. 4 | 5 | h2. Automatic Serial Numbers 6 | 7 | PowerDNS on Rails supports two modes of automatic serial number generation. 8 | 9 | h3. PowerDNS automatic serial numbers 10 | 11 | NOTE: "Read this page":http://doc.powerdns.com/generic-mypgsql-backends.html 12 | 13 | Allthough PowerDNS supports automatic serial number generation on some backends, the "Generic MySQL and PgSQL backend" does not support automatic serial number generation! 14 | 15 | h3. PowerDNS on Rails automatic serial numbers 16 | 17 | Serial numbers are automatically generated when changes are made to RR's. During batch updates, the serial number is only updated once, and NOT once for every individual change. 18 | 19 | The format of the automatic serial number follows various best-practise guidelines available online, and looks like this: 20 | 21 | YYYYMMDDNN 22 | 23 | Where YYYYMMDD should be self explanatory. NN is the change number for the day in question. The obvious limitation here is running a "roll-your-own" dyndns.org using PowerDNS on Rails. 24 | 25 | When Rails 2.1 is released we expect to be able to track "dirt objects". This means we will not update data in the database (including serial numbers) unless the data actually changed. This will keep the increments under control and make a dyndns.org style service more practical. 26 | 27 | We'll also build in configuration support for setting the length of the increment value, so actively changing zones can be handled more easily. We also plan for implementing serial modes, (ie the current one, or explicit sequences). 28 | 29 | h2. $TTL Support for RR's 30 | 31 | Each zone has a TTL column (which defaults on database level to 86400). When any RR's are created without an explicit TTL, the TTL from the parent zone will be pulled in. This gives reliable behaviour to administrators familiar with the $TTL setting. 32 | 33 | h2. $ORIGIN 34 | 35 | The zone record is the $ORIGIN. The default host of any RR is '@', this is set in the database schema as well, just like the $TTL. 36 | -------------------------------------------------------------------------------- /app/controllers/records_controller.rb: -------------------------------------------------------------------------------- 1 | class RecordsController < InheritedResources::Base 2 | 3 | belongs_to :domain 4 | respond_to :xml, :json, :js 5 | 6 | before_filter :restrict_token_movements, :except => [:create, :update, :destroy] 7 | 8 | rescue_from AuthToken::Denied do 9 | render :text => t(:message_token_not_authorized), :status => 403 10 | end 11 | 12 | protected 13 | 14 | def parent 15 | if token_user? 16 | if current_token.domain_id != params[:domain_id] 17 | raise AuthToken::Denied 18 | end 19 | current_token.domain 20 | else 21 | Domain.user( current_user ).find( params[:domain_id] ) 22 | end 23 | end 24 | 25 | def collection 26 | parent.records 27 | end 28 | 29 | def resource 30 | collection.find( params[:id] ) 31 | end 32 | 33 | def restrict_token_movements 34 | return true unless current_token 35 | 36 | render :text => t(:message_token_not_authorized), :status => 403 37 | return false 38 | end 39 | 40 | public 41 | 42 | def create 43 | @record = parent.send( "#{params[:record][:type].downcase}_records".to_sym ).new( params[:record] ) 44 | 45 | if current_token && !current_token.allow_new_records? && 46 | !current_token.can_add?( @record ) 47 | render :text => t(:message_token_not_authorized), :status => 403 48 | return 49 | end 50 | 51 | create! do 52 | if @record.persisted? 53 | # Give the token the right to undo what it just did 54 | if current_token 55 | current_token.can_change @record 56 | current_token.remove_records = true 57 | current_token.save 58 | end 59 | end 60 | end 61 | end 62 | 63 | def update 64 | if current_token && !current_token.can_change?( resource ) 65 | render :text => t(:message_token_not_authorized), :status => 403 66 | return 67 | end 68 | 69 | update! 70 | end 71 | 72 | def destroy 73 | if current_token && !current_token.can_remove?( resource ) 74 | render :text => t(:message_token_not_authorized), :status => 403 75 | return 76 | end 77 | 78 | destroy! do |format| 79 | format.html { redirect_to parent } 80 | end 81 | end 82 | 83 | # Non-CRUD methods 84 | def update_soa 85 | @domain = parent 86 | @domain.soa_record.update_attributes( params[:soa] ) 87 | end 88 | 89 | end 90 | -------------------------------------------------------------------------------- /features/support/env.rb: -------------------------------------------------------------------------------- 1 | # IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. 2 | # It is recommended to regenerate this file in the future when you upgrade to a 3 | # newer version of cucumber-rails. Consider adding your own code to a new file 4 | # instead of editing this one. Cucumber will automatically load all features/**/*.rb 5 | # files. 6 | 7 | require 'cucumber/rails' 8 | 9 | # Capybara defaults to XPath selectors rather than Webrat's default of CSS3. In 10 | # order to ease the transition to Capybara we set the default here. If you'd 11 | # prefer to use XPath just remove this line and adjust any selectors in your 12 | # steps to use the XPath syntax. 13 | Capybara.default_selector = :css 14 | 15 | # By default, any exception happening in your Rails application will bubble up 16 | # to Cucumber so that your scenario will fail. This is a different from how 17 | # your application behaves in the production environment, where an error page will 18 | # be rendered instead. 19 | # 20 | # Sometimes we want to override this default behaviour and allow Rails to rescue 21 | # exceptions and display an error page (just like when the app is running in production). 22 | # Typical scenarios where you want to do this is when you test your error pages. 23 | # There are two ways to allow Rails to rescue exceptions: 24 | # 25 | # 1) Tag your scenario (or feature) with @allow-rescue 26 | # 27 | # 2) Set the value below to true. Beware that doing this globally is not 28 | # recommended as it will mask a lot of errors for you! 29 | # 30 | ActionController::Base.allow_rescue = false 31 | 32 | # Remove/comment out the lines below if your app doesn't have a database. 33 | # For some databases (like MongoDB and CouchDB) you may need to use :truncation instead. 34 | begin 35 | DatabaseCleaner.strategy = :transaction 36 | rescue NameError 37 | raise "You need to add database_cleaner to your Gemfile (in the :test group) if you wish to use it." 38 | end 39 | 40 | # You may also want to configure DatabaseCleaner to use different strategies for certain features and scenarios. 41 | # See the DatabaseCleaner documentation for details. Example: 42 | # 43 | # Before('@no-txn,@selenium,@culerity,@celerity,@javascript') do 44 | # DatabaseCleaner.strategy = :truncation, {:except => %w[widgets]} 45 | # end 46 | # 47 | # Before('~@no-txn', '~@selenium', '~@culerity', '~@celerity', '~@javascript') do 48 | # DatabaseCleaner.strategy = :transaction 49 | # end 50 | # 51 | -------------------------------------------------------------------------------- /spec/models/macro_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Macro do 4 | 5 | context "when new" do 6 | 7 | it "should require a new" do 8 | subject.should have(1).error_on(:name) 9 | end 10 | 11 | it "should have a unique name" do 12 | m = FactoryGirl.create(:macro) 13 | subject.name = m.name 14 | subject.should have(1).error_on(:name) 15 | end 16 | 17 | it "should be disabled by default" do 18 | subject.should_not be_active 19 | end 20 | 21 | end 22 | 23 | context "when applied" do 24 | before(:each) do 25 | @target = FactoryGirl.create(:domain) 26 | 27 | @macro = FactoryGirl.create(:macro) 28 | @step_create = FactoryGirl.create(:macro_step_create, :macro => @macro, :name => 'foo') 29 | @step_update = FactoryGirl.create(:macro_step_change, :macro => @macro, :record_type => 'A', :name => 'www', :content => '127.0.0.9') 30 | @step_remove_target = FactoryGirl.create(:macro_step_remove, :macro => @macro, :record_type => 'A', :name => 'mail') 31 | @step_remove_wild = FactoryGirl.create(:macro_step_remove, :macro => @macro, :record_type => 'MX', :name => '*') 32 | 33 | @step_update2 = FactoryGirl.create(:macro_step_change, :macro => @macro, :record_type => 'A', :name => 'admin', :content => '127.0.0.10') 34 | end 35 | 36 | it "should create new RR's" do 37 | @macro.apply_to( @target ) 38 | @target.a_records.map(&:shortname).should include('foo') 39 | end 40 | 41 | it "should update existing RR's" do 42 | rr = FactoryGirl.create(:www, :domain => @target) 43 | 44 | expect { 45 | @macro.apply_to( @target ) 46 | rr.reload 47 | }.to change( rr, :content ) 48 | end 49 | 50 | it "should remove targetted RR's" do 51 | rr = FactoryGirl.create(:a, :name => 'mail', :domain => @target) 52 | 53 | @macro.apply_to( @target ) 54 | 55 | expect { 56 | rr.reload 57 | }.to raise_error( ActiveRecord::RecordNotFound ) 58 | end 59 | 60 | it "should remove existing RR's (wild card)" do 61 | FactoryGirl.create(:mx, :domain => @target) 62 | @target.mx_records(true).should_not be_empty 63 | 64 | @macro.apply_to( @target ) 65 | 66 | @target.mx_records(true).should be_empty 67 | end 68 | 69 | it "should not create RR's that were supposed to be updated but doesn't exist" do 70 | @macro.apply_to( @target ) 71 | 72 | @target.reload.a_records.detect { |a| a.name =~ /^admin/ }.should be_nil 73 | end 74 | end 75 | 76 | end 77 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | PowerdnsOnRails::Application.configure do 2 | # Settings specified here will take precedence over those in config/environment.rb 3 | 4 | # The production environment is meant for finished, "live" apps. 5 | # Code is not reloaded between requests 6 | config.cache_classes = true 7 | 8 | # Full error reports are disabled and caching is turned on 9 | config.consider_all_requests_local = false 10 | config.action_controller.perform_caching = true 11 | 12 | # Specifies the header that your server uses for sending files 13 | config.action_dispatch.x_sendfile_header = "X-Sendfile" 14 | 15 | # For nginx: 16 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' 17 | 18 | # If you have no front-end server that supports something like X-Sendfile, 19 | # just comment this out and Rails will serve the files 20 | 21 | # See everything in the log (default is :info) 22 | # config.log_level = :debug 23 | 24 | # Use a different logger for distributed setups 25 | # config.logger = SyslogLogger.new 26 | 27 | # Use a different cache store in production 28 | # config.cache_store = :mem_cache_store 29 | 30 | # Disable Rails's static asset server 31 | # In production, Apache or nginx will already do this 32 | config.serve_static_assets = false 33 | 34 | # Enable serving of images, stylesheets, and javascripts from an asset server 35 | # config.action_controller.asset_host = "http://assets.example.com" 36 | 37 | # Disable delivery errors, bad email addresses will be ignored 38 | # config.action_mailer.raise_delivery_errors = false 39 | 40 | # Enable threaded mode 41 | # config.threadsafe! 42 | 43 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 44 | # the I18n.default_locale when a translation can not be found) 45 | config.i18n.fallbacks = true 46 | 47 | # Send deprecation notices to registered listeners 48 | config.active_support.deprecation = :notify 49 | 50 | # Compress JavaScripts and CSS 51 | config.assets.compress = true 52 | 53 | # Don't fallback to assets pipeline if a precompiled asset is missed 54 | config.assets.compile = false 55 | 56 | # Generate digests for assets URLs 57 | config.assets.digest = true 58 | 59 | # Defaults to Rails.root.join("public/assets") 60 | # config.assets.manifest = YOUR_PATH 61 | 62 | # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) 63 | # config.assets.precompile += %w( search.js ) 64 | 65 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 66 | # config.force_ssl = true 67 | end 68 | -------------------------------------------------------------------------------- /lib/tasks/cucumber.rake: -------------------------------------------------------------------------------- 1 | # IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. 2 | # It is recommended to regenerate this file in the future when you upgrade to a 3 | # newer version of cucumber-rails. Consider adding your own code to a new file 4 | # instead of editing this one. Cucumber will automatically load all features/**/*.rb 5 | # files. 6 | 7 | 8 | unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks 9 | 10 | vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first 11 | $LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil? 12 | 13 | begin 14 | require 'cucumber/rake/task' 15 | 16 | namespace :cucumber do 17 | Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t| 18 | t.binary = vendored_cucumber_bin # If nil, the gem's binary is used. 19 | t.fork = true # You may get faster startup if you set this to false 20 | t.profile = 'default' 21 | end 22 | 23 | Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t| 24 | t.binary = vendored_cucumber_bin 25 | t.fork = true # You may get faster startup if you set this to false 26 | t.profile = 'wip' 27 | end 28 | 29 | Cucumber::Rake::Task.new({:rerun => 'db:test:prepare'}, 'Record failing features and run only them if any exist') do |t| 30 | t.binary = vendored_cucumber_bin 31 | t.fork = true # You may get faster startup if you set this to false 32 | t.profile = 'rerun' 33 | end 34 | 35 | desc 'Run all features' 36 | task :all => [:ok, :wip] 37 | 38 | task :statsetup do 39 | require 'rails/code_statistics' 40 | ::STATS_DIRECTORIES << %w(Cucumber\ features features) if File.exist?('features') 41 | ::CodeStatistics::TEST_TYPES << "Cucumber features" if File.exist?('features') 42 | end 43 | end 44 | desc 'Alias for cucumber:ok' 45 | task :cucumber => 'cucumber:ok' 46 | 47 | task :default => :cucumber 48 | 49 | task :features => :cucumber do 50 | STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***" 51 | end 52 | 53 | # In case we don't have ActiveRecord, append a no-op task that we can depend upon. 54 | task 'db:test:prepare' do 55 | end 56 | 57 | task :stats => 'cucumber:statsetup' 58 | rescue LoadError 59 | desc 'cucumber rake task not available (cucumber not installed)' 60 | task :cucumber do 61 | abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin' 62 | end 63 | end 64 | 65 | end 66 | -------------------------------------------------------------------------------- /lib/hijacker.rb: -------------------------------------------------------------------------------- 1 | # Hijacker class 2 | # 3 | # This class is used by RoleRequirementTestHelper to temporarily hijack a controller action for testing 4 | # 5 | # It can be used for other tests as well. 6 | # 7 | # You can contract the author with questions 8 | # Tim C. Harper - irb(main):001:0> ( 'tim_see_harperATgmail._see_om'.gsub('_see_', 'c').gsub('AT', '@') ) 9 | # 10 | # 11 | # Example usage: 12 | # hijacker = Hijacker.new(ListingsController) 13 | # hijacker.hijack_instance_method("index", "render :text => 'hello world!'" ) 14 | # get :index # will return "hello world" 15 | # hijacker.restore # put things back the way you found it 16 | 17 | class Hijacker 18 | def initialize(klass) 19 | @target_klass = klass 20 | @method_stores = {} 21 | end 22 | 23 | def hijack_class_method(method_name, eval_string = nil, arg_names = [], &block) 24 | hijack_method(class_self_instance, method_name, eval_string, arg_names, &block ) 25 | end 26 | 27 | def hijack_instance_method(method_name, eval_string = nil, arg_names = [], &block) 28 | hijack_method(@target_klass, method_name, eval_string, arg_names, &block ) 29 | end 30 | 31 | # restore all 32 | def restore 33 | @method_stores.each_pair{|klass, method_stores| 34 | method_stores.reverse_each{ |method_name, method| 35 | klass.send :undef_method, method_name 36 | klass.send :define_method, method_name, method if method 37 | } 38 | } 39 | @method_stores.clear 40 | true 41 | rescue 42 | false 43 | end 44 | 45 | protected 46 | 47 | def class_self_instance 48 | @target_klass.send :eval, "class << self; self; end;" 49 | end 50 | 51 | def hijack_method(klass, method_name, eval_string = nil, arg_names = [], &block) 52 | method_name = method_name.to_s 53 | # You have got love ruby! What other language allows you to pillage and plunder a class like this? 54 | 55 | (@method_stores[klass]||=[]) << [ 56 | method_name, 57 | klass.instance_methods.include?(method_name) && klass.instance_method(method_name) 58 | ] 59 | 60 | klass.send :undef_method, method_name 61 | if Symbol === eval_string 62 | klass.send :define_method, method_name, klass.instance_methods(eval_string) 63 | elsif String === eval_string 64 | klass.class_eval <<-EOF 65 | def #{method_name}(#{arg_names * ','}) 66 | #{eval_string} 67 | end 68 | EOF 69 | elsif block_given? 70 | klass.send :define_method, method_name, block 71 | end 72 | 73 | true 74 | rescue 75 | false 76 | end 77 | 78 | end --------------------------------------------------------------------------------