ispmail-on-rails 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +61 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +41 -0
  8. data/Rakefile +6 -0
  9. data/app/assets/images/.keep +0 -0
  10. data/app/assets/javascripts/application.js +25 -0
  11. data/app/assets/javascripts/virtual_aliases.coffee +3 -0
  12. data/app/assets/javascripts/virtual_domains.coffee +3 -0
  13. data/app/assets/javascripts/virtual_users.coffee +3 -0
  14. data/app/assets/stylesheets/_settings.scss +566 -0
  15. data/app/assets/stylesheets/application.css +21 -0
  16. data/app/assets/stylesheets/clearance.scss +6 -0
  17. data/app/assets/stylesheets/foundation_and_overrides.scss +51 -0
  18. data/app/assets/stylesheets/virtual_aliases.scss +3 -0
  19. data/app/assets/stylesheets/virtual_domains.scss +3 -0
  20. data/app/assets/stylesheets/virtual_users.scss +3 -0
  21. data/app/controllers/api/base_controller.rb +90 -0
  22. data/app/controllers/api/virtual_aliases_controller.rb +12 -0
  23. data/app/controllers/api/virtual_domains_controller.rb +15 -0
  24. data/app/controllers/api/virtual_users_controller.rb +15 -0
  25. data/app/controllers/application_controller.rb +9 -0
  26. data/app/controllers/concerns/.keep +0 -0
  27. data/app/controllers/virtual_aliases_controller.rb +6 -0
  28. data/app/controllers/virtual_domains_controller.rb +6 -0
  29. data/app/controllers/virtual_users_controller.rb +6 -0
  30. data/app/helpers/application_helper.rb +33 -0
  31. data/app/helpers/virtual_aliases_helper.rb +2 -0
  32. data/app/helpers/virtual_domains_helper.rb +2 -0
  33. data/app/helpers/virtual_users_helper.rb +2 -0
  34. data/app/mailers/.keep +0 -0
  35. data/app/models/.keep +0 -0
  36. data/app/models/concerns/.keep +0 -0
  37. data/app/models/user.rb +3 -0
  38. data/app/models/virtual_alias.rb +16 -0
  39. data/app/models/virtual_domain.rb +22 -0
  40. data/app/models/virtual_user.rb +35 -0
  41. data/app/validators/domain_name_validator.rb +21 -0
  42. data/app/validators/password_validator.rb +10 -0
  43. data/app/views/api/virtual_aliases/index.json.jbuilder +6 -0
  44. data/app/views/api/virtual_aliases/show.json.jbuilder +6 -0
  45. data/app/views/api/virtual_domains/index.json.jbuilder +4 -0
  46. data/app/views/api/virtual_domains/show.json.jbuilder +4 -0
  47. data/app/views/api/virtual_users/index.json.jbuilder +5 -0
  48. data/app/views/api/virtual_users/show.json.jbuilder +5 -0
  49. data/app/views/application/_heading.html.erb +12 -0
  50. data/app/views/clearance_mailer/change_password.html.erb +8 -0
  51. data/app/views/clearance_mailer/change_password.text.erb +5 -0
  52. data/app/views/layouts/application.html.erb +41 -0
  53. data/app/views/passwords/create.html.erb +3 -0
  54. data/app/views/passwords/edit.html.erb +18 -0
  55. data/app/views/passwords/new.html.erb +16 -0
  56. data/app/views/sessions/_form.html.erb +22 -0
  57. data/app/views/sessions/new.html.erb +6 -0
  58. data/app/views/users/_form.html.erb +9 -0
  59. data/app/views/users/new.html.erb +15 -0
  60. data/app/views/virtual_aliases/index.html.erb +58 -0
  61. data/app/views/virtual_domains/index.html.erb +56 -0
  62. data/app/views/virtual_users/_password_form.html.erb +23 -0
  63. data/app/views/virtual_users/index.html.erb +57 -0
  64. data/bin/bundle +3 -0
  65. data/bin/console +14 -0
  66. data/bin/rails +9 -0
  67. data/bin/rake +9 -0
  68. data/bin/setup +29 -0
  69. data/bin/spring +15 -0
  70. data/config.ru +4 -0
  71. data/config/application.rb +30 -0
  72. data/config/boot.rb +3 -0
  73. data/config/database.yml +31 -0
  74. data/config/environment.rb +5 -0
  75. data/config/environments/development.rb +42 -0
  76. data/config/environments/production.rb +82 -0
  77. data/config/environments/test.rb +43 -0
  78. data/config/initializers/assets.rb +11 -0
  79. data/config/initializers/backtrace_silencers.rb +7 -0
  80. data/config/initializers/clearance.rb +4 -0
  81. data/config/initializers/cookies_serializer.rb +3 -0
  82. data/config/initializers/filter_parameter_logging.rb +4 -0
  83. data/config/initializers/inflections.rb +16 -0
  84. data/config/initializers/mime_types.rb +4 -0
  85. data/config/initializers/session_store.rb +3 -0
  86. data/config/initializers/wrap_parameters.rb +14 -0
  87. data/config/locales/clearance.en.yml +59 -0
  88. data/config/locales/en.yml +23 -0
  89. data/config/routes.rb +74 -0
  90. data/config/secrets.yml +22 -0
  91. data/db/development.sqlite3 +0 -0
  92. data/db/migrate/20160321130230_create_virtual_domains.rb +13 -0
  93. data/db/migrate/20160321130250_create_virtual_users.rb +16 -0
  94. data/db/migrate/20160321130353_create_virtual_aliases.rb +15 -0
  95. data/db/migrate/20160321133546_create_users.rb +14 -0
  96. data/db/schema.rb +59 -0
  97. data/db/seeds.rb +8 -0
  98. data/ispmail-on-rails.gemspec +25 -0
  99. data/lib/assets/.keep +0 -0
  100. data/lib/ispmail/on/rails.rb +9 -0
  101. data/lib/ispmail/on/rails/version.rb +7 -0
  102. data/lib/tasks/.keep +0 -0
  103. data/log/.keep +0 -0
  104. data/public/404.html +67 -0
  105. data/public/422.html +67 -0
  106. data/public/500.html +66 -0
  107. data/public/favicon.ico +0 -0
  108. data/public/robots.txt +5 -0
  109. data/tmp/.keep +0 -0
  110. data/vendor/assets/javascripts/.keep +0 -0
  111. data/vendor/assets/stylesheets/.keep +0 -0
  112. metadata +198 -0
@@ -0,0 +1,21 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ *= require foundation_and_overrides
16
+ *= require clearance
17
+
18
+ */
19
+ .domain-heading .columns p {
20
+ margin-top: 1.35em;
21
+ }
@@ -0,0 +1,6 @@
1
+ @import "foundation";
2
+
3
+ #clearance {
4
+ @include grid-column(3);
5
+ @include grid-column-position(center);
6
+ }
@@ -0,0 +1,51 @@
1
+ @charset 'utf-8';
2
+
3
+ @import 'settings';
4
+ @import 'foundation';
5
+
6
+ // If you'd like to include motion-ui the foundation-rails gem comes prepackaged with it, uncomment the 3 @imports, if you are not using the gem you need to install the motion-ui sass package.
7
+ //
8
+ // @import 'motion-ui/motion-ui';
9
+
10
+ // We include everything by default. To slim your CSS, remove components you don't use.
11
+
12
+ @include foundation-global-styles;
13
+ @include foundation-grid;
14
+ @include foundation-typography;
15
+ @include foundation-button;
16
+ @include foundation-forms;
17
+ @include foundation-visibility-classes;
18
+ @include foundation-float-classes;
19
+ @include foundation-accordion;
20
+ @include foundation-accordion-menu;
21
+ @include foundation-badge;
22
+ @include foundation-breadcrumbs;
23
+ @include foundation-button-group;
24
+ @include foundation-callout;
25
+ @include foundation-close-button;
26
+ @include foundation-drilldown-menu;
27
+ @include foundation-dropdown;
28
+ @include foundation-dropdown-menu;
29
+ @include foundation-flex-video;
30
+ @include foundation-label;
31
+ @include foundation-media-object;
32
+ @include foundation-menu;
33
+ @include foundation-off-canvas;
34
+ @include foundation-orbit;
35
+ @include foundation-pagination;
36
+ @include foundation-progress-bar;
37
+ @include foundation-slider;
38
+ @include foundation-sticky;
39
+ @include foundation-reveal;
40
+ @include foundation-switch;
41
+ @include foundation-table;
42
+ @include foundation-tabs;
43
+ @include foundation-thumbnail;
44
+ @include foundation-title-bar;
45
+ @include foundation-tooltip;
46
+ @include foundation-top-bar;
47
+
48
+ // If you'd like to include motion-ui the foundation-rails gem comes prepackaged with it, uncomment the 3 @imports, if you are not using the gem you need to install the motion-ui sass package.
49
+ //
50
+ // @include motion-ui-transitions;
51
+ // @include motion-ui-animations;
@@ -0,0 +1,3 @@
1
+ // Place all the styles related to the VirtualAliases controller here.
2
+ // They will automatically be included in application.css.
3
+ // You can use Sass (SCSS) here: http://sass-lang.com/
@@ -0,0 +1,3 @@
1
+ // Place all the styles related to the VirtualDomains controller here.
2
+ // They will automatically be included in application.css.
3
+ // You can use Sass (SCSS) here: http://sass-lang.com/
@@ -0,0 +1,3 @@
1
+ // Place all the styles related to the VirtualUsers controller here.
2
+ // They will automatically be included in application.css.
3
+ // You can use Sass (SCSS) here: http://sass-lang.com/
@@ -0,0 +1,90 @@
1
+ # See https://blog.codelation.com/rails-restful-api-just-add-water/
2
+ module Api
3
+ class BaseController < ApplicationController
4
+ protect_from_forgery with: :null_session
5
+ before_action :set_resource, only: [:destroy, :show, :update]
6
+
7
+ # POST /api/{plural_resource_name}
8
+ def create
9
+ set_resource(resource_class.new(resource_params))
10
+
11
+ if get_resource.save
12
+ render :show, status: :created
13
+ else
14
+ render json: get_resource.errors.full_messages, status: :unprocessable_entity
15
+ end
16
+ end
17
+
18
+ # DELETE /api/{plural_resource_name}/1
19
+ def destroy
20
+ get_resource.destroy
21
+ head :no_content
22
+ end
23
+
24
+ # GET /api/{plural_resource_name}
25
+ def index
26
+ plural_resource_name = "@#{resource_name.pluralize}"
27
+ resources = resource_class.where(query_params)
28
+
29
+ instance_variable_set(plural_resource_name, resources)
30
+ instance_variable_get(plural_resource_name)
31
+ end
32
+
33
+ # GET /api/{plural_resource_name}/1
34
+ def show
35
+ get_resource
36
+ end
37
+
38
+ # PATCH/PUT /api/{plural_resource_name}/1
39
+ def update
40
+ if get_resource.update(resource_params)
41
+ render :show
42
+ else
43
+ render json: get_resource.errors.full_messages, status: :unprocessable_entity
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ # Returns the resource from the created instance variable
50
+ # @return [Object]
51
+ def get_resource
52
+ instance_variable_get("@#{resource_name}")
53
+ end
54
+
55
+ # Returns the allowed parameters for searching
56
+ # Override this method in each API controller
57
+ # to permit additional parameters to search on
58
+ # @return [Hash]
59
+ def query_params
60
+ {}
61
+ end
62
+
63
+ # The resource class based on the controller
64
+ # @return [Class]
65
+ def resource_class
66
+ @resource_class ||= resource_name.classify.constantize
67
+ end
68
+
69
+ # The singular name for the resource class based on the controller
70
+ # @return [String]
71
+ def resource_name
72
+ @resource_name ||= self.controller_name.singularize
73
+ end
74
+
75
+ # Only allow a trusted parameter "white list" through.
76
+ # If a single resource is loaded for #create or #update,
77
+ # then the controller for the resource must implement
78
+ # the method "#{resource_name}_params" to limit permitted
79
+ # parameters for the individual model.
80
+ def resource_params
81
+ @resource_params ||= self.send("#{resource_name}_params")
82
+ end
83
+
84
+ # Use callbacks to share common setup or constraints between actions.
85
+ def set_resource(resource = nil)
86
+ resource ||= resource_class.find(params[:id])
87
+ instance_variable_set("@#{resource_name}", resource)
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,12 @@
1
+ module Api
2
+ class VirtualAliasesController < Api::BaseController
3
+
4
+ private
5
+
6
+ def virtual_alias_params
7
+ params.require(:virtual_alias).permit(:source, :destination, :domain_id)
8
+ end
9
+
10
+ end
11
+ end
12
+
@@ -0,0 +1,15 @@
1
+ module Api
2
+ class VirtualDomainsController < Api::BaseController
3
+
4
+ private
5
+
6
+ def virtual_domain_params
7
+ params.require(:virtual_domain).permit(:name)
8
+ end
9
+
10
+ def query_params
11
+ params.permit(:name)
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Api
2
+ class VirtualUsersController < Api::BaseController
3
+
4
+ private
5
+
6
+ def virtual_user_params
7
+ params.require(:virtual_user).permit(:email, :domain_id, :new_password)
8
+ end
9
+
10
+ def query_params
11
+ params.permit(:email)
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ class ApplicationController < ActionController::Base
2
+ include Clearance::Controller
3
+
4
+ before_action :require_login
5
+
6
+ # Prevent CSRF attacks by raising an exception.
7
+ # For APIs, you may want to use :null_session instead.
8
+ protect_from_forgery with: :exception
9
+ end
File without changes
@@ -0,0 +1,6 @@
1
+ class VirtualAliasesController < ApplicationController
2
+ def index
3
+ @domain = VirtualDomain.find_by_name(params.require(:id))
4
+ @virtual_alias = VirtualAlias.new(virtual_domain: @domain)
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class VirtualDomainsController < ApplicationController
2
+ def index
3
+ @domains = VirtualDomain.includes(:virtual_users, :virtual_aliases).all
4
+ @domain = VirtualDomain.new
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class VirtualUsersController < ApplicationController
2
+ def index
3
+ @domain = VirtualDomain.find_by_name(params.require(:id))
4
+ @virtual_user = VirtualUser.new(virtual_domain: @domain)
5
+ end
6
+ end
@@ -0,0 +1,33 @@
1
+ module ApplicationHelper
2
+ def form_error_callout(form_id)
3
+ form_callout = <<-ajax_error
4
+ $('form##{form_id}').bind('ajax:error', function(evt, data, status, xhr){
5
+ $("#messages").html(
6
+ '#{flash_messages('data.responseJSON.join("<br/>")', true, true)}'
7
+ );
8
+ $('#messages').foundation('open');
9
+ })
10
+ ajax_error
11
+ form_callout.html_safe
12
+ end
13
+
14
+ # Render HTML flash messages callout
15
+ def flash_messages(text, warning=false, javascript=false)
16
+ html = <<-messages
17
+ <div id="flash" class="callout #{warning ? 'warning' : 'secondary' }" data-closable>
18
+ REPLACEME
19
+ <button class="close-button" aria-label="Dismiss alert" type="button" data-close>
20
+ <span aria-hidden="true">&times;</span>
21
+ </button>
22
+ </div>
23
+ messages
24
+ if javascript
25
+ js = html.split("\n").map(&:strip).join("'+'")
26
+ return js.sub(/'REPLACEME'/, text)
27
+ else
28
+ html.sub!(/REPLACEME/, text)
29
+ return html.html_safe
30
+ end
31
+ html.html_safe
32
+ end
33
+ end
@@ -0,0 +1,2 @@
1
+ module VirtualAliasesHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ module VirtualDomainsHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ module VirtualUsersHelper
2
+ end
File without changes
File without changes
File without changes
@@ -0,0 +1,3 @@
1
+ class User < ActiveRecord::Base
2
+ include Clearance::User
3
+ end
@@ -0,0 +1,16 @@
1
+ class VirtualAlias < ActiveRecord::Base
2
+ validates :source, presence: true, domain_name: true, email: true
3
+ validates :destination, presence: true, email: true
4
+ validates :domain_id, presence: true
5
+
6
+ belongs_to :virtual_domain, foreign_key: :domain_id
7
+
8
+ before_validation :complete_emails
9
+
10
+ private
11
+ def complete_emails
12
+ self.source += "@#{virtual_domain.name}" unless source =~ /@/
13
+ self.destination += "@#{virtual_domain.name}" unless destination =~ /@/
14
+ end
15
+
16
+ end
@@ -0,0 +1,22 @@
1
+ # Email domains
2
+ class VirtualDomain < ActiveRecord::Base
3
+ validates :name, presence: true, uniqueness: true, domain_name: true
4
+
5
+ has_many :virtual_users, foreign_key: :domain_id
6
+ has_many :virtual_aliases, foreign_key: :domain_id
7
+
8
+ attr_reader :mx_records
9
+
10
+ # What are the DNS MX records for this VirtualDomain
11
+ def mx_records
12
+ @mx_records ||= Resolv::DNS.open do |dns|
13
+ records = dns.getresources(name, Resolv::DNS::Resource::IN::MX)
14
+ records.map(&:exchange).join(" ")
15
+ end
16
+ end
17
+
18
+ # Does this VirtualDomain's MX record resolve to the local machine?
19
+ def does_resolve_to_host?
20
+ mx_records.include? Socket.gethostname
21
+ end
22
+ end
@@ -0,0 +1,35 @@
1
+ # These are the actual email addresses with passwords
2
+ class VirtualUser < ActiveRecord::Base
3
+ validates_presence_of :domain_id
4
+ validates :email, uniqueness: true, presence: true, domain_name: true, email: true
5
+ validates :new_password, password: true
6
+
7
+ belongs_to :virtual_domain, foreign_key: :domain_id
8
+
9
+ attr_accessor :new_password
10
+
11
+ before_validation :complete_email
12
+ before_save :encrypt_new_password
13
+
14
+ # Capture any misplaced password changes
15
+ def password=(value)
16
+ self.new_password = value
17
+ end
18
+
19
+ def password_changing?
20
+ !new_password.blank?
21
+ end
22
+
23
+ private
24
+ def complete_email
25
+ unless email =~ /@/
26
+ self.email += "@#{virtual_domain.name}"
27
+ end
28
+ end
29
+
30
+ def encrypt_new_password
31
+ if password_changing?
32
+ self["password"] = UnixCrypt::SHA256.build(new_password)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ # Validates the attribute's value includes the virtual_domain.name after an at symbol
2
+ class DomainNameValidator < ActiveModel::EachValidator
3
+ def validate_each(record, attribute, value)
4
+ if record.class == VirtualDomain
5
+ domain_check = Regexp.new "^[a-zA-Z0-9][a-zA-Z0-9-_]{0,61}[a-zA-Z0-9]{0,1}\.([a-zA-Z]{1,6}|[a-zA-Z0-9-]{1,30}\.[a-zA-Z]{2,})$"
6
+ tld = value.split(".").last
7
+ unless IANA::TLD.valid?(tld)
8
+ record.errors[attribute] << (options[:message] || ".#{tld} is not accepted by IANA as a Top Level Domain")
9
+ end
10
+ unless record.name =~ domain_check
11
+ record.errors[attribute] << (options[:message] || "is not a valid domain name")
12
+ end
13
+ else
14
+ domain = record.virtual_domain.name
15
+ domain_check = Regexp.new "@"+domain+"$"
16
+ unless value =~ domain_check
17
+ record.errors[attribute] << (options[:message] || "is not under #{domain}")
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ # Validates the attribute's value is an acceptable password
2
+ class PasswordValidator < ActiveModel::EachValidator
3
+ def validate_each(record, attribute, value)
4
+ if record.password_changing? && value.to_s.size <= 10
5
+ record.errors[attribute] << (options[:message] || "must be minimum 10 characters long")
6
+ elsif record.password.blank? && !record.password_changing?
7
+ record.errors[attribute] << (options[:message] || "must be set")
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,6 @@
1
+ json.virtual_aliases @virtual_aliases do |virtual_alias|
2
+ json.id virtual_alias.id
3
+ json.domain_id virtual_alias.domain_id
4
+ json.source virtual_alias.source
5
+ json.destination virtual_alias.destination
6
+ end