diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 00000000..cd57a8b9 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.1.5 diff --git a/Gemfile b/Gemfile index 0af8c2b2..678de9e7 100644 --- a/Gemfile +++ b/Gemfile @@ -100,3 +100,13 @@ end # ruby code style checker gem 'rubocop' +# optimize queries +gem "bullet", :group => "development" + +gem 'public_activity' + +# decorators +gem 'draper', '~> 1.3' + +# foreman helper +gem 'foreman' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index 208abc55..5e83386e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -42,7 +42,7 @@ GEM activerecord (>= 2.3.9) axlsx (>= 1.0.13) i18n (>= 0.4.1) - addressable (2.3.6) + addressable (2.3.8) arel (5.0.1.20140414130214) ast (2.0.0) astrolabe (1.3.0) @@ -51,11 +51,14 @@ GEM htmlentities (~> 4.3.1) nokogiri (>= 1.4.1) rubyzip (~> 1.0.0) - bcrypt (3.1.9) + bcrypt (3.1.10) bootstrap-kaminari-views (0.0.5) kaminari (>= 0.13) rails (>= 3.1) builder (3.2.2) + bullet (4.14.4) + activesupport (>= 3.0.0) + uniform_notifier (>= 1.6.0) capybara (2.4.4) mime-types (>= 1.16) nokogiri (>= 1.3.3) @@ -67,23 +70,23 @@ GEM activesupport (>= 3.2.0) json (>= 1.7) mime-types (>= 1.16) - celluloid (0.15.2) - timers (~> 1.1.0) - codemirror-rails (4.5) + celluloid (0.16.0) + timers (~> 4.0.0) + codemirror-rails (5.0) railties (>= 3.0, < 5) coderay (1.1.0) coffee-rails (4.1.0) coffee-script (>= 2.2.0) railties (>= 4.0.0, < 5.0) - coffee-script (2.3.0) + coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.8.0) + coffee-script-source (1.9.1.1) commonjs (0.2.7) - connection_pool (2.0.0) - d3-rails (3.4.13) + connection_pool (2.2.0) + d3-rails (3.5.5) railties (>= 3.1) - database_cleaner (1.1.1) + database_cleaner (1.4.1) devise (3.4.1) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -92,10 +95,15 @@ GEM thread_safe (~> 0.1) warden (~> 1.2.3) diff-lcs (1.2.5) - domain_name (0.5.22) + domain_name (0.5.23) unf (>= 0.0.5, < 1.0.0) + draper (1.3.0) + actionpack (>= 3.0) + activemodel (>= 3.0) + activesupport (>= 3.0) + request_store (~> 1.0.3) erubis (2.7.0) - execjs (2.2.2) + execjs (2.5.2) factory_girl (4.5.0) activesupport (>= 3.0.0) factory_girl_rails (4.5.0) @@ -103,37 +111,44 @@ GEM railties (>= 3.0.0) faker (1.4.3) i18n (~> 0.5) - ffi (1.9.6) + ffi (1.9.8) + foreman (0.78.0) + thor (~> 0.19.1) formatador (0.2.5) geokit (1.9.0) multi_json (>= 1.3.2) - googlecharts (1.6.8) - guard (2.10.1) + googlecharts (1.6.10) + guard (2.12.5) formatador (>= 0.2.4) listen (~> 2.7) lumberjack (~> 1.0) + nenv (~> 0.1) + notiffany (~> 0.0) pry (>= 0.9.12) + shellany (~> 0.0) thor (>= 0.18.1) - guard-compat (0.3.0) - guard-rspec (4.4.2) + guard-compat (1.2.1) + guard-rspec (4.5.0) guard (~> 2.1) - guard-compat (~> 0.1) + guard-compat (~> 1.1) rspec (>= 2.99.0, < 4.0) - hike (1.2.3) - htmlentities (4.3.2) + hitimes (1.2.2) + htmlentities (4.3.3) http-cookie (1.0.2) domain_name (~> 0.5) - i18n (0.6.11) - jquery-datatables-rails (3.0.0) + i18n (0.7.0) + jquery-datatables-rails (3.2.0) + actionpack (>= 3.1) jquery-rails + railties (>= 3.1) sass-rails jquery-rails (3.1.2) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) - jquery-ui-rails (5.0.2) + jquery-ui-rails (5.0.3) railties (>= 3.2.16) - json (1.8.1) - kaminari (0.16.1) + json (1.8.2) + kaminari (0.16.3) actionpack (>= 3.0.0) activesupport (>= 3.0.0) launchy (2.4.3) @@ -143,14 +158,15 @@ GEM less-rails (2.6.0) actionpack (>= 3.1) less (~> 2.6.0) - letter_opener (1.2.0) + letter_opener (1.3.0) launchy (~> 2.2) - letter_opener_web (1.2.3) + letter_opener_web (1.3.0) + actionmailer (>= 3.2) letter_opener (~> 1.0) - rails (>= 3.2) + railties (>= 3.2) libv8 (3.16.14.7) - listen (2.8.3) - celluloid (>= 0.15.2) + listen (2.10.0) + celluloid (~> 0.16.0) rb-fsevent (>= 0.9.3) rb-inotify (>= 0.9) lumberjack (1.0.9) @@ -167,31 +183,34 @@ GEM webrobots (>= 0.0.9, < 0.2) method_source (0.8.2) mime-types (2.4.3) - mini_portile (0.6.1) - minitest (5.4.3) - multi_json (1.10.1) - mysql2 (0.3.17) + mini_portile (0.6.2) + minitest (5.6.0) + multi_json (1.11.0) + mysql2 (0.3.18) + nenv (0.2.0) nested_form (0.3.2) net-http-digest_auth (1.4) net-http-persistent (2.9.4) - nokogiri (1.6.4.1) + nokogiri (1.6.6.2) mini_portile (~> 0.6.0) + notiffany (0.0.6) + nenv (~> 0.1) + shellany (~> 0.0) ntlm-http (0.1.1) - open_uri_redirections (0.1.4) + open_uri_redirections (0.2.1) orm_adapter (0.5.0) - paper_trail (3.0.6) - activerecord (>= 3.0, < 5.0) - activesupport (>= 3.0, < 5.0) - parser (2.2.0.pre.5) + paper_trail (4.0.0.beta2) + activerecord (>= 3.0, < 6.0) + activesupport (>= 3.0, < 6.0) + parser (2.2.0.3) ast (>= 1.1, < 3.0) - slop (~> 3.4, >= 3.4.5) pdf-core (0.2.5) - powerpack (0.0.9) + powerpack (0.1.0) prawn (1.2.1) pdf-core (~> 0.2.5) ttfunk (~> 1.2.0) prawn-table (0.1.2) - protected_attributes (1.0.8) + protected_attributes (1.0.9) activemodel (>= 4.0.1, < 5.0) pry (0.10.1) coderay (~> 1.1.0) @@ -199,11 +218,16 @@ GEM slop (~> 3.4) pry-nav (0.2.4) pry (>= 0.9.10, < 0.11.0) - ptools (1.2.6) + ptools (1.3.2) + public_activity (1.4.2) + actionpack (>= 3.0.0) + activerecord (>= 3.0) + i18n (>= 0.5.0) + railties (>= 3.0.0) rack (1.5.2) rack-protection (1.5.3) rack - rack-test (0.6.2) + rack-test (0.6.3) rack (>= 1.0) rails (4.1.7) actionmailer (= 4.1.7) @@ -221,87 +245,90 @@ GEM rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.0.0) - rake (10.3.2) + rake (10.4.2) rb-fsevent (0.9.4) rb-inotify (0.9.5) ffi (>= 0.5.0) - redis (3.1.0) - redis-namespace (1.5.1) + redis (3.2.1) + redis-namespace (1.5.2) redis (~> 3.0, >= 3.0.4) ref (1.0.5) + request_store (1.0.5) responders (1.1.2) railties (>= 3.2, < 4.2) - rspec (3.1.0) - rspec-core (~> 3.1.0) - rspec-expectations (~> 3.1.0) - rspec-mocks (~> 3.1.0) - rspec-core (3.1.7) - rspec-support (~> 3.1.0) - rspec-expectations (3.1.2) + rspec (3.2.0) + rspec-core (~> 3.2.0) + rspec-expectations (~> 3.2.0) + rspec-mocks (~> 3.2.0) + rspec-core (3.2.3) + rspec-support (~> 3.2.0) + rspec-expectations (3.2.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.1.0) - rspec-mocks (3.1.3) - rspec-support (~> 3.1.0) - rspec-rails (3.1.0) - actionpack (>= 3.0) - activesupport (>= 3.0) - railties (>= 3.0) - rspec-core (~> 3.1.0) - rspec-expectations (~> 3.1.0) - rspec-mocks (~> 3.1.0) - rspec-support (~> 3.1.0) - rspec-support (3.1.2) - rubocop (0.26.1) + rspec-support (~> 3.2.0) + rspec-mocks (3.2.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.2.0) + rspec-rails (3.2.1) + actionpack (>= 3.0, < 4.3) + activesupport (>= 3.0, < 4.3) + railties (>= 3.0, < 4.3) + rspec-core (~> 3.2.0) + rspec-expectations (~> 3.2.0) + rspec-mocks (~> 3.2.0) + rspec-support (~> 3.2.0) + rspec-support (3.2.2) + rubocop (0.30.0) astrolabe (~> 1.3) - parser (>= 2.2.0.pre.4, < 3.0) - powerpack (~> 0.0.6) + parser (>= 2.2.0.1, < 3.0) + powerpack (~> 0.1) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.4) - ruby-progressbar (1.6.0) + ruby-progressbar (1.7.5) rubyzip (1.0.0) - sass (3.2.19) - sass-rails (4.0.4) + sass (3.4.13) + sass-rails (5.0.3) railties (>= 4.0.0, < 5.0) - sass (~> 3.2.2) - sprockets (~> 2.8, < 2.12) - sprockets-rails (~> 2.0) + sass (~> 3.1) + sprockets (>= 2.8, < 4.0) + sprockets-rails (>= 2.0, < 4.0) + tilt (~> 1.1) searchbing (0.2.4) - sidekiq (3.2.6) - celluloid (= 0.15.2) - connection_pool (>= 2.0.0) + shellany (0.0.1) + sidekiq (3.3.3) + celluloid (>= 0.16.0) + connection_pool (>= 2.1.1) json redis (>= 3.0.6) redis-namespace (>= 1.3.1) - sinatra (1.4.5) + sinatra (1.4.6) rack (~> 1.4) rack-protection (~> 1.4) - tilt (~> 1.3, >= 1.3.4) + tilt (>= 1.3, < 3) slop (3.6.0) - sprockets (2.11.3) - hike (~> 1.2) - multi_json (~> 1.0) + sprockets (3.0.1) rack (~> 1.0) - tilt (~> 1.1, != 1.3.0) - sprockets-rails (2.2.0) + sprockets-rails (2.2.4) actionpack (>= 3.0) activesupport (>= 3.0) sprockets (>= 2.8, < 4.0) - therubyracer (0.12.1) + therubyracer (0.12.2) libv8 (~> 3.16.14.0) ref thor (0.19.1) - thread_safe (0.3.4) + thread_safe (0.3.5) tilt (1.4.1) - timers (1.1.0) + timers (4.0.1) + hitimes ttfunk (1.2.2) tzinfo (1.2.2) thread_safe (~> 0.1) - uglifier (2.5.3) + uglifier (2.7.1) execjs (>= 0.3.0) json (>= 1.8.0) unf (0.1.4) unf_ext unf_ext (0.0.6) + uniform_notifier (1.8.0) warden (1.2.3) rack (>= 1.0) webrobots (0.1.1) @@ -315,6 +342,7 @@ DEPENDENCIES acts_as_xlsx axlsx bootstrap-kaminari-views + bullet capybara carrierwave codemirror-rails @@ -322,9 +350,11 @@ DEPENDENCIES d3-rails database_cleaner devise + draper (~> 1.3) execjs factory_girl_rails faker + foreman geokit googlecharts guard-rspec @@ -345,6 +375,7 @@ DEPENDENCIES pry pry-nav ptools + public_activity rails (= 4.1.7) rspec-rails rubocop diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..733c0723 --- /dev/null +++ b/LICENSE @@ -0,0 +1,675 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff --git a/Procfile b/Procfile new file mode 100644 index 00000000..f5bce1de --- /dev/null +++ b/Procfile @@ -0,0 +1,2 @@ +web: bundle exec rails server +worker: bundle exec sidekiq -C config/sidekiq.yml diff --git a/app/assets/javascripts/blasts.js b/app/assets/javascripts/blasts.js new file mode 100644 index 00000000..e69de29b diff --git a/app/assets/javascripts/campaigns.js b/app/assets/javascripts/campaigns.js index a13b5030..30704ec8 100644 --- a/app/assets/javascripts/campaigns.js +++ b/app/assets/javascripts/campaigns.js @@ -10,7 +10,6 @@ $( document ).ready(function() { var myValue = $(this).val(); switch (myValue) { case 'google': - $('#campaign_email_settings_attributes_smtp_server').val('smtp.gmail.com'); $('#campaign_email_settings_attributes_smtp_server_out').val('smtp.gmail.com'); $('#campaign_email_settings_attributes_authentication').val('plain'); $('#campaign_email_settings_attributes_domain').val('gmail.com'); @@ -19,19 +18,16 @@ $( document ).ready(function() { $('#campaign_email_settings_attributes_smtp_port').val('587'); break; case 'outlook': - $('#campaign_email_settings_attributes_smtp_server').val('smtp.outlook.com'); $('#campaign_email_settings_attributes_smtp_server_out').val('smtp.outlook.com'); $('#campaign_email_settings_attributes_smtp_port').val('25'); break; case 'godaddy': - $('#campaign_email_settings_attributes_smtp_server').val('smtp.secureserver.net'); $('#campaign_email_settings_attributes_smtp_server_out').val('smtpout.secureserver.net'); $('#campaign_email_settings_attributes_smtp_port').val('3535'); break; case 'sendgrid': - $('#campaign_email_settings_attributes_smtp_server').val('smtp.sendgrid.net'); $('#campaign_email_settings_attributes_smtp_server_out').val('smtp.sendgrid.net'); - $('#campaign_email_settings_attributes_smtp_port').val('25'); + $('#campaign_email_settings_attributes_smtp_port').val('2525'); break; } }); diff --git a/app/assets/javascripts/letters.js b/app/assets/javascripts/letters.js new file mode 100644 index 00000000..e69de29b diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 45fdabc4..a34c4b3d 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -162,3 +162,15 @@ A:hover font-size: 12px; list-style: square; } + +.breadcrumb { + margin-top: 10px; +} + +.pad-bot { + margin-bottom: 10px; +} + +.nav-tabs { + padding-top: 10px; +} \ No newline at end of file diff --git a/app/assets/stylesheets/blasts.css b/app/assets/stylesheets/blasts.css new file mode 100644 index 00000000..e69de29b diff --git a/app/assets/stylesheets/letter_opener_web/application.css b/app/assets/stylesheets/letter_opener_web/application.css index a829b8ad..7a0105fb 100644 --- a/app/assets/stylesheets/letter_opener_web/application.css +++ b/app/assets/stylesheets/letter_opener_web/application.css @@ -13,7 +13,7 @@ } #mail { - width: 64%%; + width: 64%; height: 100%; position: absolute; border: solid 1px #ccc; diff --git a/app/assets/stylesheets/letters.css b/app/assets/stylesheets/letters.css new file mode 100644 index 00000000..e69de29b diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 101d33c4..91119f0c 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,13 +1,16 @@ class ApplicationController < ActionController::Base + include PublicActivity::StoreController + protect_from_forgery - + before_action :configure_permitted_parameters, if: :devise_controller? before_filter :authenticate_admin! before_filter :system_status add_flash_types :warning - protected +protected + def configure_permitted_parameters devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:username) } devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :name, :email, :password, :password_confirmation) } @@ -25,10 +28,12 @@ def system_status q = Sidekiq::Stats.new.enqueued @redis = true if q > 0 and !@sidekiq - flash[:warning] = "You have #{ActionController::Base.helpers.pluralize(q, 'job')} enqueued, but sidekiq is not running" + flash[:warning] = "You have #{ActionController::Base.helpers.pluralize(q, 'job')} enqueued, but Sidekiq is not running" end rescue Redis::CannotConnectError => e + logger.error e.message @redis = false + flash[:warning] = e.message if e.message =~ /timeout/i end end diff --git a/app/controllers/campaigns_controller.rb b/app/controllers/campaigns_controller.rb index 0ad7a18b..ae8f3479 100644 --- a/app/controllers/campaigns_controller.rb +++ b/app/controllers/campaigns_controller.rb @@ -9,12 +9,12 @@ def index def list # grab the campaigns and sort by created_at date - @campaigns = Campaign.order("created_at DESC") + @campaigns = Campaign.includes(:victims, :admin).order("created_at DESC") end def home - # grab only the launched campaigns - @campaigns = Campaign.launched.order("created_at DESC").limit(50) + @activities = PublicActivity::Activity.includes(:trackable, :owner).order('created_at DESC').limit(30) + @campaigns = Campaign.launched.order("created_at DESC").limit(10) end def show @@ -33,6 +33,8 @@ def new def create @campaign = Campaign.new(params[:campaign]) + @campaign.admin_id = current_admin.id + if @campaign.save redirect_to @campaign, notice: "Campaign Created" else @@ -46,6 +48,9 @@ def edit def update @campaign = Campaign.find(params[:id]) + if params[:campaign][:email_settings_attributes][:smtp_password].blank? && !@campaign.email_settings.smtp_password.blank? + params[:campaign][:email_settings_attributes][:smtp_password] = @campaign.email_settings.smtp_password + end if @campaign.update_attributes(params[:campaign]) redirect_to @campaign, notice: "Campaign Updated" else @@ -78,6 +83,10 @@ def options end end + def activity + @activities = PublicActivity::Activity.includes(:trackable, :owner).order('created_at DESC').page(params[:page]).per(30) + end + def victims @victims = Victim.where("campaign_id = ? and archive = ?", params[:id], false) end diff --git a/app/controllers/email_controller.rb b/app/controllers/email_controller.rb index c031a80c..392c5010 100644 --- a/app/controllers/email_controller.rb +++ b/app/controllers/email_controller.rb @@ -1,105 +1,75 @@ class EmailController < ApplicationController - PREVIEW = 0 - ACTIVE = 1 - def index send_email render('send') end - def preview - @campaign = Campaign.find(params[:id]) - @blast = @campaign.blasts.create(test: true) - if GlobalSettings.asynchronous? - begin - PhishingFrenzyMailer.delay.phish(@campaign.id, @campaign.test_victim.email_address, @blast.id, PREVIEW) - flash[:notice] = "Campaign test email queued for preview" - rescue Redis::CannotConnectError => e - flash[:error] = "Sidekiq cannot connect to Redis. Emails were not queued." - rescue::NoMethodError - flash[:error] = "Template is missing an email file, upload and create new email" - rescue => e - flash[:error] = "Template Issue: #{e}" - end - else - begin - PhishingFrenzyMailer.phish(@campaign.id, @campaign.test_victim.email_address, @blast.id, PREVIEW) - flash[:notice] = "Campaign test email available for preview" - rescue::NoMethodError - flash[:error] = "Template is missing an email file, upload and create new email" - rescue => e - flash[:error] = "Template Issue: #{e}" + def test_delivery(action, redirect, async_success_notice, sync_success_notice) + campaign = Campaign.find(params[:id]) + blast = campaign.blasts.create(test: true) + begin + # There is no reason we would want to queue a preview. + if GlobalSettings.asynchronous? && action != PhishingFrenzyMailer::PREVIEW + MailWorker.perform_async(campaign.id, campaign.test_victim.email_address, blast.id, action) + flash[:notice] = async_success_notice + else + PhishingFrenzyMailer.phish(campaign.id, campaign.test_victim.email_address, blast.id, action) + flash[:notice] = sync_success_notice end + rescue Redis::CannotConnectError => e + flash[:error] = "Sidekiq cannot connect to Redis. Emails were not queued." + rescue::NoMethodError + flash[:error] = "Template is missing an email file, upload and create new email" + rescue => e + flash[:error] = "Generic Template Issue: #{e}" end - redirect_to "/letter_opener" + + redirect_to redirect + end + + def preview + test_delivery(PhishingFrenzyMailer::PREVIEW, + "/letter_opener", + "Campaign test email queued for preview", + "Campaign test email available for preview") end def test - @campaign = Campaign.find(params[:id]) - @blast = @campaign.blasts.create(test: true) - if GlobalSettings.asynchronous? - begin - PhishingFrenzyMailer.delay.phish(@campaign.id, @campaign.test_victim.email_address, @blast.id, ACTIVE) - flash[:notice] = "Campaign test email queued for test" - rescue Redis::CannotConnectError => e - flash[:error] = "Sidekiq cannot connect to Redis. Emails were not queued." - rescue::NoMethodError - flash[:error] = "Template is missing an email file, upload and create new email" - rescue => e - flash[:error] = "Generic Template Issue: #{e}" - end - else - begin - PhishingFrenzyMailer.phish(@campaign.id, @campaign.test_victim.email_address, @blast.id, ACTIVE) - flash[:notice] = "Campaign test email sent" - rescue::NoMethodError - flash[:error] = "Template is missing an email file, upload and create new email" - rescue => e - flash[:error] = "Generic Template Issue: #{e}" - end - end - redirect_to :back + test_delivery(PhishingFrenzyMailer::ACTIVE, + :back, + "Campaign test email queued for test", + "Campaign test email sent") end def launch - @campaign = Campaign.find(params[:id]) - @campaign.update_attributes(active: true, email_sent: true) - if @campaign.errors.present? + campaign = Campaign.find(params[:id]) + if campaign.errors.present? render template: "/campaigns/show" return false end - @blast = @campaign.blasts.create(test: false) + campaign.update_attributes(active: true, email_sent: true) + blast = campaign.blasts.create(test: false) victims = Victim.where("campaign_id = ? and archive = ?", params[:id], false) - if GlobalSettings.asynchronous? - begin - victims.each do |target| - PhishingFrenzyMailer.delay.phish(@campaign.id, target, @blast.id, ACTIVE) - target.update_attribute(:sent, true) + begin + victims.each do |target| + if GlobalSettings.asynchronous? + MailWorker.perform_async(campaign.id, target.email_address, blast.id, PhishingFrenzyMailer::ACTIVE) + else + PhishingFrenzyMailer.phish(campaign.id, target.email_address, blast.id, PhishingFrenzyMailer::ACTIVE) end - flash[:notice] = "Campaign blast launched" - rescue Redis::CannotConnectError => e - flash[:error] = "Sidekiq cannot connect to Redis. Emails were not queued." - rescue::NoMethodError - flash[:error] = "Template is missing an email file, upload and create new email" - rescue => e - flash[:error] = "Generic Template Issue: #{e}" - end - else - begin - victims.each do |target| - PhishingFrenzyMailer.phish(@campaign.id, target, @blast, ACTIVE) - target.update_attribute(:sent, true) - end - flash[:notice] = "Campaign blast launched" - rescue::NoMethodError - flash[:error] = "Template is missing an email file, upload and create new email" - rescue => e - flash[:error] = "Generic Template Issue: #{e}" + target.update_attribute(:sent, true) end + flash[:notice] = "Campaign blast launched" + rescue Redis::CannotConnectError => e + flash[:error] = "Sidekiq cannot connect to Redis. Emails were not queued." + rescue::NoMethodError + flash[:error] = "Template is missing an email file, upload and create new email" + rescue => e + flash[:error] = "Generic Template Issue: #{e}" end - redirect_to :back + redirect_to :back end end diff --git a/app/controllers/templates_controller.rb b/app/controllers/templates_controller.rb index 44eec8da..efff212b 100644 --- a/app/controllers/templates_controller.rb +++ b/app/controllers/templates_controller.rb @@ -6,7 +6,7 @@ def index end def list - @templates = Template.all + @templates = Template.includes(:admin).all end def show @@ -20,6 +20,8 @@ def new def create @template = Template.new(params[:template]) + @template.admin_id = current_admin.id + if @template.save flash[:notice] = "Template Created" redirect_to(:action => 'list') @@ -30,13 +32,16 @@ def create end def edit + @campaign = campaign_present @template = Template.find(params[:id]) end def update + @campaign = campaign_present + campaign_id = @campaign.present? ? @campaign.id : nil @template = Template.find(params[:id]) if @template.update_attributes(params[:template]) - redirect_to edit_template_path, notice: "Template Updated" + redirect_to edit_template_path(campaign_id: campaign_id), notice: "Template Updated" else render('edit') end @@ -162,11 +167,17 @@ def upload end def edit_email + @campaign = campaign_present @attachment = Attachment.find(params[:format]) - attachment_location = File.join(Rails.root.to_s, "public", "uploads", "attachment", "file", params[:format], "*") + attachment_location = @attachment.file.current_path begin @attachment_content = File.read(Dir.glob(attachment_location)[0]) + + unless @attachment_content.is_utf8? + flash[:warning] = "Cannot Edit Files that are not UTF-8 sequence" + redirect_to :back + end rescue flash[:warning] = "Issue Reading Attachment File" redirect_to :back @@ -188,6 +199,10 @@ def update_attachment private + def campaign_present + params[:campaign_id].present? ? Campaign.find_by_id(params[:campaign_id]) : nil + end + def copy_template(template) # generate random string random_string = (0...8).map { (65 + rand(26)).chr }.join diff --git a/app/decorators/campaign_decorator.rb b/app/decorators/campaign_decorator.rb new file mode 100644 index 00000000..66a646af --- /dev/null +++ b/app/decorators/campaign_decorator.rb @@ -0,0 +1,20 @@ +class CampaignDecorator < Draper::Decorator + include IconDecorator + + def owner + object.admin ? object.admin.username : "Unknown" + end + + def active_icon + object.active ? + h.image_tag('green-light.png', size: '11x11', alt: 'actives') : + h.image_tag('red-light.png', size: '11x11', alt: 'inactives') + end + + def emails_icon + object.victims.present? ? + h.image_tag('green-light.png', :size => '11x11', alt: 'emails') : + h.image_tag('red-light.png', :size => '11x11', alt: 'noemails') + end + +end diff --git a/app/decorators/icon_decorator.rb b/app/decorators/icon_decorator.rb new file mode 100644 index 00000000..e5c08487 --- /dev/null +++ b/app/decorators/icon_decorator.rb @@ -0,0 +1,23 @@ +module IconDecorator + + def campaign_icon + h.content_tag(:i, nil, class: 'glyphicon glyphicon-envelope') + end + + def template_icon + h.content_tag(:i, nil, class: 'glyphicon glyphicon-tags') + end + + def report_icon + h.content_tag(:i, nil, class: 'glyphicon glyphicon-stats') + end + + def options_icon + h.content_tag(:i, nil, class: 'glyphicon glyphicon-search') + end + + def edit_icon + h.content_tag(:i, nil, class: 'glyphicon glyphicon-edit') + end + +end diff --git a/app/decorators/public_activity/activity_decorator.rb b/app/decorators/public_activity/activity_decorator.rb new file mode 100644 index 00000000..0af3e1bd --- /dev/null +++ b/app/decorators/public_activity/activity_decorator.rb @@ -0,0 +1,14 @@ +class PublicActivity::ActivityDecorator < Draper::Decorator + include IconDecorator + + def owner + object.owner ? object.owner.name : 'Unknown' + end + + def format_date + h.content_tag :span, class: "small pull-right" do + object.created_at.strftime("%m/%d %H:%M %p") + end + end + +end \ No newline at end of file diff --git a/app/decorators/report_decorator.rb b/app/decorators/report_decorator.rb new file mode 100644 index 00000000..528ac70a --- /dev/null +++ b/app/decorators/report_decorator.rb @@ -0,0 +1,3 @@ +class ReportDecorator < Draper::Decorator + include IconDecorator +end diff --git a/app/decorators/template_decorator.rb b/app/decorators/template_decorator.rb new file mode 100644 index 00000000..3b8c675b --- /dev/null +++ b/app/decorators/template_decorator.rb @@ -0,0 +1,8 @@ +class TemplateDecorator < Draper::Decorator + include IconDecorator + + def owner + object.admin ? object.admin.username : "Unknown" + end + +end diff --git a/app/mailers/phishing_frenzy_mailer.rb b/app/mailers/phishing_frenzy_mailer.rb index 7965987f..24b06a08 100644 --- a/app/mailers/phishing_frenzy_mailer.rb +++ b/app/mailers/phishing_frenzy_mailer.rb @@ -1,51 +1,66 @@ class PhishingFrenzyMailer < ActionMailer::Base - + PREVIEW = 0 ACTIVE = 1 - def phish(campaign_id, target, blast_id, method=PREVIEW) + def phish(campaign_id, target_email, blast_id, method=PREVIEW) @campaign = Campaign.find(campaign_id) @display_from = @campaign.email_settings.display_from @date = Time.now.to_formatted_s(:long_ordinal) - track = @campaign.campaign_settings.track_uniq_visitors? phishing_url = @campaign.email_settings.phishing_url - target.class == String ? @target = Victim.new(email_address: target) : @target = target blast = @campaign.blasts.find(blast_id) @campaign.template.images.each do |image| attachments.inline[image[:file]] = File.read(image.file.current_path) end - if method==ACTIVE - uid = victim_uid(@target, campaign_id) + @campaign.template.file_attachments.each do |attachment| + attachments[File.basename attachment.file.to_s] = { + content_type: attachment.file.content_type, + body: File.read(attachment.file.current_path) + } + end + + @target = get_victim(target_email, campaign_id) + uid = @target.uid.to_s + + if @campaign.campaign_settings.track_uniq_visitors? @url = "#{phishing_url}?uid=#{uid}" - @image_url = PhishingFramework::SITE_URL + "/reports/image/#{uid}.png" - bait = mail( - to: @target.email_address, - from: "\ #{@campaign.email_settings.display_from}\ \<#{@campaign.email_settings.from}\>", - subject: @campaign.email_settings.subject, - template_path: @campaign.template.email_template_path, - template_name: @campaign.template.email_files.first[:file], - delivery_method: :smtp - ) - + @image_url = "#{GlobalSettings.first.site_url}/reports/image/#{uid}.png" + else + @url = phishing_url + # Don't know a default non-tracking image? + @image_url = "#{GlobalSettings.first.site_url}/reports/image/000000.png" + end + + mail_opts = { + to: @target.email_address, + from: "\ #{@campaign.email_settings.display_from}\ \<#{@campaign.email_settings.from}\>", + subject: @campaign.email_settings.subject, + template_path: @campaign.template.email_template_path, + template_name: @campaign.template.email_files.first[:file], + } + + mail_opts[:reply_to] = @campaign.email_settings.reply_to unless @campaign.email_settings.reply_to.blank? + + case method + when ACTIVE + mail_opts[:delivery_method] = :smtp + bait = mail(mail_opts) # if no authentication is selected send anonymous smtp if @campaign.email_settings.authentication == "none" bait.delivery_method.settings.merge!(campaign_anonymous_smtp_settings) else bait.delivery_method.settings.merge!(campaign_smtp_settings) end - cast(blast, bait) + when PREVIEW + mail_opts[:delivery_method] = :letter_opener_web + bait = mail(mail_opts) else - uid = victim_uid(@target, campaign_id) - bait = mail( - to: @target.email_address, - subject: @campaign.email_settings.subject, - template_path: @campaign.template.email_template_path, - template_name: @campaign.template.email_files.first[:file], - delivery_method: :letter_opener_web) - cast(blast, bait) + raise RuntimeError, 'Unknown mailer action' end + + cast(blast, bait) end def cast(blast, bait) @@ -73,16 +88,11 @@ def cast(blast, bait) end def campaign_smtp_settings - { - :openssl_verify_mode => @campaign.email_settings.openssl_verify_mode_class, - address: @campaign.email_settings.smtp_server_out, - port: @campaign.email_settings.smtp_port, + campaign_anonymous_smtp_settings.merge({ user_name: @campaign.email_settings.smtp_username, password: @campaign.email_settings.smtp_password, authentication: @campaign.email_settings.authentication.to_sym, - enable_starttls_auto: @campaign.email_settings.enable_starttls_auto, - return_response: true - } + }) end def campaign_anonymous_smtp_settings @@ -95,13 +105,13 @@ def campaign_anonymous_smtp_settings } end - def victim_uid(target, campaign_id) - victim = Victim.where(:email_address => target.email_address, :campaign_id => campaign_id) - if victim.present? - uid = victim.first.uid - else - uid = "000000" - end - return uid + def get_victim(email, campaign_id) + victim = Victim.find_by(email_address: email, campaign_id: campaign_id) + victim = Victim.new(email_address: email, + uid: '000000', + firstname: 'Firstname', + lastname: 'Lastname') unless victim + + victim end -end \ No newline at end of file +end diff --git a/app/models/attachment.rb b/app/models/attachment.rb index eaead5e9..f4e375a5 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -5,4 +5,9 @@ class Attachment < ActiveRecord::Base belongs_to :attachable, :polymorphic => true mount_uploader :file, FileUploader + + FUNCTIONS = [['Website File', 'website'], + ['E-mail', 'email'], + ['Image Attachment', 'attachment'], + ['File Attachment', 'file_attachment']] end diff --git a/app/models/bait.rb b/app/models/bait.rb index 449cf5c9..7a14851d 100644 --- a/app/models/bait.rb +++ b/app/models/bait.rb @@ -1,4 +1,4 @@ class Bait < ActiveRecord::Base attr_accessible :blast_id, :from, :message, :status, :to - belongs_to :blast + belongs_to :blast, counter_cache: true end diff --git a/app/models/campaign.rb b/app/models/campaign.rb index 92f718d8..6ee2a246 100644 --- a/app/models/campaign.rb +++ b/app/models/campaign.rb @@ -2,7 +2,11 @@ require 'zip' class Campaign < ActiveRecord::Base + include PublicActivity::Model + tracked owner: ->(controller, model) { controller && controller.current_admin } + # relationships + belongs_to :admin belongs_to :template has_one :campaign_settings, dependent: :destroy has_one :email_settings, dependent: :destroy @@ -13,13 +17,13 @@ class Campaign < ActiveRecord::Base has_many :smtp_communications has_many :baits, through: :blasts has_many :visits, through: :victims - + accepts_nested_attributes_for :email_settings, allow_destroy: true accepts_nested_attributes_for :campaign_settings, allow_destroy: true accepts_nested_attributes_for :ssl, allow_destroy: true#, :reject_if => proc {|attributes| attributes['filename'].blank?} # allow mass asignment - attr_accessible :name, :description, :active, :emails, :scope, :template_id, :test_email, :ssl_attributes,:email_sent, :email_settings_attributes, :campaign_settings_attributes + attr_accessible :name, :description, :active, :emails, :scope, :template_id, :test_email, :ssl_attributes, :email_sent, :admin_id, :email_settings_attributes, :campaign_settings_attributes # named scopes scope :active, -> { where(active: true) } @@ -38,9 +42,13 @@ class Campaign < ActiveRecord::Base :length => {:maximum => 255} validates :emails, :length => {:maximum => 60000} + + validate :validate_email_addresses + validates :scope, :numericality => {:greater_than_or_equal_to => 0}, :length => {:maximum => 4}, :allow_nil => true + def create_deps Ssl.functions.each do |function| newSSL = ssl.new @@ -99,63 +107,53 @@ def test_victim private - def parse_email_addresses - if not self.emails.blank? - entry = self.emails.split("\r\n")[0] - if entry.scan(/,/).count == 0 - # email - parse_single_csv - elsif entry.scan(/,/).count == 1 - # firstname, email - parse_double_csv - elsif entry.scan(/,/).count == 2 - # firstname, lastname, email - parse_triple_csv + def validate_email_addresses + return if self.emails.blank? + count = self.emails.lines.first.split(',').count + + if count > 3 + errors.add(:emails, "Invalid input format '#{self.emails.lines.first}'") + return + end + + self.emails.each_line do |l| + split = l.split(',').map(&:strip) + unless split.count == count + errors.add(:emails, "Email format is not consistent '#{l}'") + end + + unless split.last =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i + errors.add(:emails, "Invalid email format '#{split.last}'") end - - # clear the Campaigns.emails holder - self.update_attribute(:emails, " ") end end - def parse_single_csv - victims = self.emails.split("\r\n") - victims.each do |v| + def parse_email_addresses + return if self.emails.blank? + count = self.emails.lines.first.split(',').count + return if count > 3 + + self.emails.each_line do |l| + split = l.split(',').map(&:strip) + next unless split.count == count + victim = Victim.new victim.campaign_id = self.id victim.firstname = "" victim.lastname = "" - victim.email_address = v - victim.save - end - end - - def parse_double_csv - victims = self.emails.split("\r\n") - victims.each do |v| - firstname = v.split(",")[0].strip - email = v.split(",")[1].strip - victim = Victim.new - victim.campaign_id = self.id - victim.firstname = firstname - victim.email_address = email + victim.email_address = split.last + case count + when 2 + victim.firstname = split.first + when 3 + victim.firstname = split.first + victim.lastname = split[1] + end victim.save end - end - def parse_triple_csv - victims = self.emails.split("\r\n") - victims.each do |v| - firstname = v.split(",")[0].strip - lastname = v.split(",")[1].strip - email = v.split(",")[2].strip - victim = Victim.new - victim.campaign_id = self.id - victim.firstname = firstname - victim.lastname = lastname - victim.email_address = email - victim.save - end + # clear the Campaigns.emails holder + self.update_attribute(:emails, " ") end def vhost_text(campaign, virtual_host_type) @@ -277,7 +275,7 @@ def tag_beef def select_beef_url return campaign_settings.beef_url unless campaign_settings.beef_url.empty? return GlobalSettings.first.beef_url unless GlobalSettings.first.beef_url.empty? - "#{PhishingFramework::SITE_URL}:3000/hook.js" + return "#{GlobalSettings.first.site_url}:3000/hook.js" end def deployment_directory @@ -312,7 +310,7 @@ def cleanup_apache def reload_apache # reload apache restart_apache = GlobalSettings.first.command_apache_restart - system("#{restart_apache} > /dev/null") + Process.spawn("#{restart_apache} > /dev/null") end end diff --git a/app/models/email_settings.rb b/app/models/email_settings.rb index af4ad85f..57ece8a3 100644 --- a/app/models/email_settings.rb +++ b/app/models/email_settings.rb @@ -3,7 +3,7 @@ class EmailSettings < ActiveRecord::Base attr_accessible :campaign_id, :to, :cc, :bcc, :from, :display_from, :subject, :phishing_url, :smtp_server, :smtp_username, :smtp_password, :smtp_port, :smtp_server_out, - :openssl_verify_mode, :domain, :authentication, :enable_starttls_auto + :openssl_verify_mode, :domain, :authentication, :enable_starttls_auto, :reply_to validates :campaign_id, :presence => true, :length => {:maximum => 255} diff --git a/app/models/global_settings.rb b/app/models/global_settings.rb index 52bd657a..cc679b29 100644 --- a/app/models/global_settings.rb +++ b/app/models/global_settings.rb @@ -1,6 +1,6 @@ class GlobalSettings < ActiveRecord::Base - attr_accessible :command_apache_restart, :command_apache_vhosts, :command_apache_status, :sites_enabled_path, :smtp_timeout, :asynchronous, :bing_api, :beef_url, :beef_apikey + attr_accessible :site_url, :command_apache_restart, :command_apache_vhosts, :command_apache_status, :sites_enabled_path, :smtp_timeout, :asynchronous, :bing_api, :beef_url, :beef_apikey, :reports_refresh validates :command_apache_restart, :presence => true, :length => {:maximum => 255} validates :command_apache_vhosts, :presence => true, :length => {:maximum => 255} diff --git a/app/models/system_monitor.rb b/app/models/system_monitor.rb index b45d655d..479a19a4 100644 --- a/app/models/system_monitor.rb +++ b/app/models/system_monitor.rb @@ -19,8 +19,7 @@ def self.metasploit # determine if BeeF is running def self.beef - beef_output = `ps aux | grep beef | grep -v color` - #TODO anti: fix this, also add RESTful API auth via credentials, then store the api token in the DB and add it to JS + beef_output = `ps aux | grep '[b]eef'` beef_output =~ /beef/ end @@ -30,8 +29,9 @@ def self.sidekiq pid = File.read(pid_file) Process.getpgid(pid.to_i) rescue Exception => e - Rails.logger.error e + Rails.logger.error %Q(Sidekiq is not running, or pid file, '#{pid_file}' is invalid: + #{e.class} - #{e.message}) false end end -end \ No newline at end of file +end diff --git a/app/models/template.rb b/app/models/template.rb index fdeac264..6657cc57 100644 --- a/app/models/template.rb +++ b/app/models/template.rb @@ -1,8 +1,12 @@ class Template < ActiveRecord::Base + include PublicActivity::Model + tracked owner: ->(controller, model) { controller && controller.current_admin } + + belongs_to :admin has_many :campaigns has_many :attachments, as: :attachable, dependent: :destroy - attr_accessible :name, :description, :notes, :attachments_attributes, :directory_index + attr_accessible :name, :description, :notes, :attachments_attributes, :directory_index, :admin_id validates :name, presence: true, length: { :maximum => 255 } validates_with TemplateValidator @@ -35,6 +39,10 @@ def images end end + def file_attachments + attachments.where(function: 'file_attachment') + end + def index_file directory_index.blank? ? 'index.php' : directory_index end diff --git a/app/models/victim.rb b/app/models/victim.rb index e92fa009..62cc67df 100644 --- a/app/models/victim.rb +++ b/app/models/victim.rb @@ -3,6 +3,7 @@ class Victim < ActiveRecord::Base has_many :visits, dependent: :destroy validates_format_of :email_address, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i + validates_uniqueness_of :email_address, scope: :campaign_id before_create :default_values attr_accessible :email_address, :uid, :campaign_id, :firstname, :lastname, :hb_id diff --git a/app/views/admin/global_settings.html.erb b/app/views/admin/global_settings.html.erb index f0de554b..f1945d80 100644 --- a/app/views/admin/global_settings.html.erb +++ b/app/views/admin/global_settings.html.erb @@ -9,6 +9,13 @@ <%= form_for(:global_settings, :url => {:action => 'update_global_settings'}) do |f| %> + + + + + + @@ -30,6 +37,11 @@ + + + + + diff --git a/app/views/admin/logins.html.erb b/app/views/admin/logins.html.erb index 6526f7c7..22e86c14 100644 --- a/app/views/admin/logins.html.erb +++ b/app/views/admin/logins.html.erb @@ -4,7 +4,7 @@

<%= @admin.name %> Logins

<%= pluralize(@logins.size, 'login') %> logged out of <%= @admin.sign_in_count %> logins total
-
Application Site URL<%= f.text_field(:site_url, placeholder: 'https://phishingfrenzy.local', + required: true, class: 'form-control') %>
Apache Restart Command <%= f.text_field(:sites_enabled_path, class: 'form-control') %>
Reports Refresh Time<%= f.text_field(:reports_refresh, class: 'form-control') %>
SMTP Timeout
+
@@ -19,4 +19,4 @@ <% end %>
Date IP
- \ No newline at end of file + diff --git a/app/views/campaigns/_blasts.html.erb b/app/views/campaigns/_blasts.html.erb index f9370ba3..89e7164e 100644 --- a/app/views/campaigns/_blasts.html.erb +++ b/app/views/campaigns/_blasts.html.erb @@ -20,12 +20,12 @@ - <% campaign.blasts.order("id DESC").each do |blast| %> + <% campaign.blasts.includes(:baits).order("id DESC").each do |blast| %> <%= link_to "#{time_ago_in_words(blast.created_at)} ago", blast %> <%= blast.test? %> <%= blast.baits.size %> - <%= blast.smtp_failures %> + <%= link_to blast.smtp_failures, blast %> <% end %> diff --git a/app/views/campaigns/_buttons.html.erb b/app/views/campaigns/_buttons.html.erb index fe6431f5..2aae87c9 100644 --- a/app/views/campaigns/_buttons.html.erb +++ b/app/views/campaigns/_buttons.html.erb @@ -13,7 +13,7 @@ <% if campaign.template.present? %>
- <%= link_to("Edit Email", edit_template_path(campaign.template), class: 'btn btn-default') %> + <%= link_to("Edit Email", edit_template_path(campaign.template, campaign_id: campaign.id), class: 'btn btn-default') %>
<% end %> \ No newline at end of file diff --git a/app/views/campaigns/_email_settings.html.erb b/app/views/campaigns/_email_settings.html.erb index 92b3c2de..18aabb06 100644 --- a/app/views/campaigns/_email_settings.html.erb +++ b/app/views/campaigns/_email_settings.html.erb @@ -51,6 +51,20 @@ +
+
+

Reply To:

+
+
+ +
+
+ <%= ff.text_field(:reply_to, + placeholder: 'email@phishingfrenzy.local', + class: 'form-control') %> +
+
+

Phishing URL:

@@ -60,7 +74,7 @@
<%= ff.text_field(:phishing_url, - placeholder: 'site.phishingfrenzy.local', + placeholder: 'https://site.phishingfrenzy.local', class: 'form-control') %>
@@ -83,4 +97,4 @@ - \ No newline at end of file + diff --git a/app/views/campaigns/_phishing_options.html.erb b/app/views/campaigns/_phishing_options.html.erb index 24f96ede..f55d9292 100644 --- a/app/views/campaigns/_phishing_options.html.erb +++ b/app/views/campaigns/_phishing_options.html.erb @@ -53,7 +53,7 @@
- <%= ff.text_field(:beef_url, class: 'form-control') %> + <%= ff.text_field(:beef_url, value: GlobalSettings.first.beef_url, class: 'form-control') %>
diff --git a/app/views/campaigns/_smtp_settings.html.erb b/app/views/campaigns/_smtp_settings.html.erb index d0849d0d..7b412719 100644 --- a/app/views/campaigns/_smtp_settings.html.erb +++ b/app/views/campaigns/_smtp_settings.html.erb @@ -27,20 +27,6 @@ -
-
-

SMTP Server:

-
-
- -
-
- <%= ff.text_field(:smtp_server, - placeholder: 'mail.phishingfrenzy.local', - class: 'form-control') %> -
-
-

SMTP Outbound Server:

@@ -107,7 +93,6 @@
<%= ff.password_field(:smtp_password, placeholder: 'password', - value: @campaign.email_settings.smtp_password, class: 'form-control') %>
@@ -154,4 +139,4 @@ <% end %>
- \ No newline at end of file + diff --git a/app/views/campaigns/activity.html.erb b/app/views/campaigns/activity.html.erb new file mode 100644 index 00000000..d754aac6 --- /dev/null +++ b/app/views/campaigns/activity.html.erb @@ -0,0 +1,4 @@ +

Activity Logs

+ +<%= paginate @activities, theme: 'twitter-bootstrap-3' %> +<%= render_activities @activities %> \ No newline at end of file diff --git a/app/views/campaigns/home.html.erb b/app/views/campaigns/home.html.erb index fd91b15b..092fc70c 100644 --- a/app/views/campaigns/home.html.erb +++ b/app/views/campaigns/home.html.erb @@ -1,11 +1,26 @@ -<% @page_title = "Home" %> - -

Launched Phishing Campaigns

- -
-
- <%= render partial: "campaign", collection: @campaigns %> -
-
\ No newline at end of file +<% @page_title = "Dashboard" %> + + + + + +
+
+

Launched Phishing Campaigns

+ +
+
+ <%= render partial: "campaign", collection: @campaigns %> +
+
+
+ +
+

Recent Activity

+ <%= render_activities @activities %> + <%= link_to "All Activity", activity_campaigns_path, class: "btn btn-primary btn-block" %> +
+
\ No newline at end of file diff --git a/app/views/campaigns/home_old.html.erb b/app/views/campaigns/home_old.html.erb index 9e2126da..98c9bdab 100644 --- a/app/views/campaigns/home_old.html.erb +++ b/app/views/campaigns/home_old.html.erb @@ -6,7 +6,7 @@
<%= pluralize(@campaigns.size, 'campaign') %> active
<% if @campaigns.size > 0 %> - +
diff --git a/app/views/campaigns/list.html.erb b/app/views/campaigns/list.html.erb index dc3d5e17..63cf5f50 100644 --- a/app/views/campaigns/list.html.erb +++ b/app/views/campaigns/list.html.erb @@ -13,12 +13,12 @@ -
  Client
+
- + @@ -28,15 +28,11 @@ <% @campaigns.each do |campaign| %> - + - - - + + + <% @campaigns.each do |campaign| %> - + - + diff --git a/app/views/reports/passwords.html.erb b/app/views/reports/passwords.html.erb index 55f6ef0f..1e762b25 100644 --- a/app/views/reports/passwords.html.erb +++ b/app/views/reports/passwords.html.erb @@ -9,7 +9,7 @@ - + diff --git a/app/views/reports/stats.html.erb b/app/views/reports/stats.html.erb index 742b9321..1c7e2211 100644 --- a/app/views/reports/stats.html.erb +++ b/app/views/reports/stats.html.erb @@ -281,7 +281,7 @@ $(document).ready(function() { }); - }, 3000); + }, <%= (GlobalSettings.first.reports_refresh.to_f * 1000.0).to_i %>); }); /* diff --git a/app/views/reports/tags.txt.erb b/app/views/reports/tags.txt.erb index 6d6b9912..0aaaead0 100644 --- a/app/views/reports/tags.txt.erb +++ b/app/views/reports/tags.txt.erb @@ -2,6 +2,30 @@ // Turn off all error reporting error_reporting(0); +if (isset($_GET['uid'])) { + $uid = $_GET['uid']; + } else { + header('404 Not Found', true, 404); + echo "404 Page Not Found"; + exit(); + } + +function get_ip() { + if (function_exists('apache_request_headers')) { + $headers = apache_request_headers(); + } else { + $headers = $_SERVER; + } + if (array_key_exists('X-Forwarded-For',$headers) && filter_var($headers['X-Forwarded-For'],FILTER_VALIDATE_IP,FILTER_FLAG_IPV4)) { + $the_ip = $headers['X-Forwarded-For']; + } elseif (array_key_exists('HTTP_X_FORWARDED_FOR',$headers) && filter_var($headers['HTTP_X_FORWARDED_FOR'],FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { + $the_ip = $headers['HTTP_X_FORWARDED_FOR']; + } else { + $the_ip = filter_var($_SERVER['REMOTE_ADDR'],FILTER_VALIDATE_IP,FILTER_FLAG_IPV4); + } + return $the_ip; + } + $password = $_POST['PasswordForm']; $username = $_POST['UsernameForm']; @@ -9,11 +33,10 @@ if ($password != '') { $creds = 'user:' . $username . ' password:' . $password; } -$uid = $_GET['uid']; -$ip = $_SERVER['REMOTE_ADDR']; +$ip = get_ip(); $browser = $_SERVER['HTTP_USER_AGENT']; $host = $_SERVER['HTTP_HOST']; -$url = "<%= PhishingFramework::SITE_URL %>" . '/reports/results/'; +$url = "<%= GlobalSettings.first.site_url %>" . '/reports/results/'; $data = array('uid' => $uid, 'browser_info' => $browser, 'ip_address' => $ip, 'extra' => $creds); // use key 'http' even if you send the request to https://... diff --git a/app/views/templates/_form.html.erb b/app/views/templates/_form.html.erb index 6994e6e1..ec1d3130 100644 --- a/app/views/templates/_form.html.erb +++ b/app/views/templates/_form.html.erb @@ -1,49 +1,54 @@ <%= error_messages_for(@template) %> -
ClientDescriptionOwner Active Emails Actions
<%= campaign.decorate.campaign_icon %> <%= campaign.name %><%= truncate(campaign.description) %> - <%= campaign.active ? image_tag('green-light.png', :size => '11x11') : image_tag('red-light.png', :size => '11x11') %> - - <%= campaign.victims.present? ? image_tag('green-light.png', :size => '11x11') : image_tag('red-light.png', :size => '11x11') %> - <%= campaign.decorate.owner %><%= campaign.decorate.active_icon %><%= campaign.decorate.emails_icon %>
<%= link_to("".html_safe, diff --git a/app/views/email/launch_email.html.erb b/app/views/email/launch_email.html.erb index 6d14c3ec..9afd61a7 100644 --- a/app/views/email/launch_email.html.erb +++ b/app/views/email/launch_email.html.erb @@ -4,4 +4,4 @@

No emails assigned to the To field

<% else %> <%= @email_settings.to %> -<% end %> \ No newline at end of file +<% end %> diff --git a/app/views/layouts/_navigation.html.erb b/app/views/layouts/_navigation.html.erb index dcac2026..52d3baa1 100644 --- a/app/views/layouts/_navigation.html.erb +++ b/app/views/layouts/_navigation.html.erb @@ -30,8 +30,8 @@ Admin
  • <%= link_to "Logout", main_app.destroy_admin_session_path, :method => :delete %>
  • diff --git a/app/views/layouts/_system_status.html.erb b/app/views/layouts/_system_status.html.erb index b8cea171..48ce9960 100644 --- a/app/views/layouts/_system_status.html.erb +++ b/app/views/layouts/_system_status.html.erb @@ -30,8 +30,8 @@ <% end %> <% end %> <% if @beef %> -
  • <%= link_to("#{PhishingFramework::SITE_URL}:3000/ui/panel", target: "_blank") do %> - BeeF +
  • <%= link_to("#{GlobalSettings.first.site_url}:3000/ui/panel", target: "_blank") do %> + BeEF <% end %>
  • <% else %>
  • <%= link_to("#") do %> @@ -59,4 +59,4 @@ <% end %>
  • - \ No newline at end of file + diff --git a/app/views/public_activity/campaign/_create.html.erb b/app/views/public_activity/campaign/_create.html.erb new file mode 100644 index 00000000..1eb7c38b --- /dev/null +++ b/app/views/public_activity/campaign/_create.html.erb @@ -0,0 +1,9 @@ +
  • + <% if a.trackable %> + <%= a.decorate.campaign_icon %> <%= a.decorate.owner %> added campaign + <%= link_to a.trackable.name, campaign_path(a.trackable) %> + <% else %> + <%= a.decorate.campaign_icon %> A campaign that is currently deleted was added by <%= a.decorate.owner %> + <% end %> + <%= a.decorate.format_date %> +
  • \ No newline at end of file diff --git a/app/views/public_activity/campaign/_destroy.html.erb b/app/views/public_activity/campaign/_destroy.html.erb new file mode 100644 index 00000000..2b0a728c --- /dev/null +++ b/app/views/public_activity/campaign/_destroy.html.erb @@ -0,0 +1,9 @@ +
  • + <% if a.trackable %> + <%= a.decorate.campaign_icon %> <%= a.decorate.owner %> deleted campaign + <%= link_to a.trackable.name, campaign_path(a.trackable) %> + <% else %> + <%= a.decorate.campaign_icon %> A campaign that is currently deleted was removed by <%= a.decorate.owner %> + <% end %> + <%= a.decorate.format_date %> +
  • \ No newline at end of file diff --git a/app/views/public_activity/campaign/_update.html.erb b/app/views/public_activity/campaign/_update.html.erb new file mode 100644 index 00000000..2de776e3 --- /dev/null +++ b/app/views/public_activity/campaign/_update.html.erb @@ -0,0 +1,9 @@ +
  • + <% if a.trackable %> + <%= a.decorate.campaign_icon %> <%= a.decorate.owner %> edited campaign + <%= link_to a.trackable.name, campaign_path(a.trackable) %> + <% else %> + <%= a.decorate.campaign_icon %> A campaign that is currently deleted was edited by <%= a.decorate.owner %> + <% end %> + <%= a.decorate.format_date %> +
  • \ No newline at end of file diff --git a/app/views/public_activity/template/_create.html.erb b/app/views/public_activity/template/_create.html.erb new file mode 100644 index 00000000..7cdbb955 --- /dev/null +++ b/app/views/public_activity/template/_create.html.erb @@ -0,0 +1,9 @@ +
  • + <% if a.trackable %> + <%= a.decorate.template_icon %> <%= a.decorate.owner %> added template + <%= link_to a.trackable.name, campaign_path(a.trackable) %> + <% else %> + <%= a.decorate.template_icon %> A template that is currently deleted was added by <%= a.decorate.owner %> + <% end %> + <%= a.decorate.format_date %> +
  • \ No newline at end of file diff --git a/app/views/public_activity/template/_destroy.html.erb b/app/views/public_activity/template/_destroy.html.erb new file mode 100644 index 00000000..8327c296 --- /dev/null +++ b/app/views/public_activity/template/_destroy.html.erb @@ -0,0 +1,9 @@ +
  • + <% if a.trackable %> + <%= a.decorate.template_icon %> <%= a.decorate.owner %> deleted template + <%= link_to a.trackable.name, campaign_path(a.trackable) %> + <% else %> + <%= a.decorate.template_icon %> A template that is currently deleted was removed by <%= a.decorate.owner %> + <% end %> + <%= a.decorate.format_date %> +
  • \ No newline at end of file diff --git a/app/views/public_activity/template/_update.html.erb b/app/views/public_activity/template/_update.html.erb new file mode 100644 index 00000000..ebf67682 --- /dev/null +++ b/app/views/public_activity/template/_update.html.erb @@ -0,0 +1,9 @@ +
  • + <% if a.trackable %> + <%= a.decorate.template_icon %> <%= a.decorate.owner %> edited template + <%= link_to a.trackable.name, campaign_path(a.trackable) %> + <% else %> + <%= a.decorate.template_icon %> A template that is currently deleted was edited by <%= a.decorate.owner %> + <% end %> + <%= a.decorate.format_date %> +
  • \ No newline at end of file diff --git a/app/views/reports/list.html.erb b/app/views/reports/list.html.erb index 3048e876..956a6ba8 100644 --- a/app/views/reports/list.html.erb +++ b/app/views/reports/list.html.erb @@ -20,14 +20,13 @@
    <%= campaign.decorate.report_icon %> <%= campaign.name %> <%= campaign.sent %> <%= campaign.clicks %><%= campaign.active ? image_tag('green-light.png', size: '11x11') : - image_tag('red-light.png', size: '11x11') %><%= campaign.decorate.active_icon %> - <%= link_to("Stats", stats_reports_path(id: campaign.id), + <%= link_to(campaign.decorate.options_icon, stats_reports_path(id: campaign.id), class: 'btn btn-primary btn-xs') %>
    Email PasswordBroswerBrowser IP Address Seen
    - - - - - - - - - - - - - - - - - - - - -
    Name<%= f.text_field(:name, required: true, class: "form-control") %>
    Description<%= f.text_area(:description, :size => "31x2", class: "form-control") %>
    Notes - - <%= f.text_area(:notes, :size => "31x2", class: "form-control") %>
    Index - - <%= f.text_field(:directory_index, class: "form-control") %>
    -

    Template Files

    +
    + + + + + + + + + + + + + + + + + + + + + +
    Name<%= f.text_field(:name, required: true, class: "form-control", placeholder: "Template Name") %>
    Description<%= f.text_area(:description, class: "form-control", placeholder: "Description...") %>
    Notes + + <%= f.text_area(:notes, class: "form-control", placeholder: "Notes") %>
    Index + + <%= f.text_field(:directory_index, required: true, class: "form-control", placeholder: "index.php") %>
    +
    -
    - <%= f.fields_for :attachments, f.object.attachments.order(:function) do |attachment_form| %> -
    -
    - <%= attachment_form.link_to_remove "Remove" %> -
    -
    - <%= link_to attachment_form.object[:file], edit_email_templates_path(attachment_form.object[:id]) unless attachment_form.object[:file].nil? %> -
    -
    - <%= attachment_form.select(:function, %w(website email attachment), {}, {class: "form-control"}) %> -
    -
    - <%= attachment_form.file_field :file, class: 'form-control btn btn-default btn-file' %> +
    +

    Template Files

    + +
    + <%= f.fields_for :attachments, f.object.attachments.order(:function) do |attachment_form| %> +
    +
    + <%= attachment_form.link_to_remove "Remove" %> +
    +
    + <%= link_to attachment_form.object[:file], edit_email_templates_path(attachment_form.object[:id], campaign_id: params[:campaign_id]) unless attachment_form.object[:file].nil? %> +
    +
    + <%= attachment_form.select(:function, Attachment::FUNCTIONS, {}, {class: "form-control"}) %> +
    +
    + <%= attachment_form.file_field :file, class: 'form-control btn btn-default btn-file' %> +
    -
    - <% end %> -
    -<%= f.link_to_add "Add attachment", :attachments, "data-target" => "#attachments" %> \ No newline at end of file + <% end %> +
    + <%= f.link_to_add "Add attachment", :attachments, "data-target" => "#attachments" %> +
    \ No newline at end of file diff --git a/app/views/templates/edit.html.erb b/app/views/templates/edit.html.erb index ed6495c3..c2350489 100644 --- a/app/views/templates/edit.html.erb +++ b/app/views/templates/edit.html.erb @@ -1,15 +1,19 @@ <% @page_title = "Update Template" %> -<%= link_to("<< Back to List", {:action => 'list'}, :class => 'back-link') %> +<% if @campaign.present? %> + +<% end %> + +

    Update Phishing Template

    -
    <%= nested_form_for @template, :html => {:multipart => true} do |f| %> - <%= render(:partial => "form", :locals => {:f => f}) %> -

    - <%= submit_tag("Update Template", class: 'btn btn-primary') %> -

    + <%= render(:partial => "form", :locals => {:f => f}) %> + <%= hidden_field_tag :campaign_id, params[:campaign_id] %> + + <%= submit_tag("Update Template", class: 'btn btn-primary btn-block pad-bot') %> <% end %>
    \ No newline at end of file diff --git a/app/views/templates/edit_email.html.erb b/app/views/templates/edit_email.html.erb index 2dd3d704..ec3663f9 100644 --- a/app/views/templates/edit_email.html.erb +++ b/app/views/templates/edit_email.html.erb @@ -1,21 +1,22 @@ <% @page_title = "Edit Template File" %> -<%= link_to("<< Back", - edit_template_path(@attachment.attachable_id), - :class => 'back-link') %> +<% if @campaign.present? %> + +<% end %> -
    -
    - <%= form_for(:attachment, :url => {:action => 'update_attachment', :format => params[:format]}) do |f| %> -
    -

    - Edit Attachment - <%= File.basename(@attachment.file.to_s) %> - <%= submit_tag("Save", class: 'btn btn-primary') %> -

    -
    - - <%= text_area_tag("attachment_content", @attachment_content, - rows: 35 ,class: 'form-control') %> - <% end %> +<%= form_for(:attachment, :url => {:action => 'update_attachment', :format => params[:format]}) do |f| %> +
    +

    + Edit Attachment - <%= File.basename(@attachment.file.to_s) %> + <%= submit_tag("Save", class: 'btn btn-primary') %> +

    -
    + + <%= text_area_tag("attachment_content", @attachment_content, + rows: 35 ,class: 'form-control') %> +<% end %> + + diff --git a/app/views/templates/list.html.erb b/app/views/templates/list.html.erb index 7070b947..b1e9f7ae 100644 --- a/app/views/templates/list.html.erb +++ b/app/views/templates/list.html.erb @@ -18,7 +18,7 @@ Name - Description + Owner Index Actions @@ -26,15 +26,15 @@ <% @templates.each do |template| %> - + <%= template.decorate.template_icon %> <%= template.name %> - <%= truncate(template.description) %> + <%= template.decorate.owner %> <%= template.index_file %> - <%= link_to("".html_safe, + <%= link_to(template.decorate.edit_icon, {:action => 'edit', :id => template.id}, data: {placement: 'bottom'}, class: 'btn btn-primary btn-xs', rel: 'tooltip', title: 'Edit Template') %> - <%= link_to("".html_safe, + <%= link_to(template.decorate.options_icon, {:action => 'show', :id => template.id}, data: {placement: 'bottom'}, class: 'btn btn-default btn-xs', rel: 'tooltip', title: 'Show Template') %> <%= link_to("".html_safe, diff --git a/app/workers/mail_worker.rb b/app/workers/mail_worker.rb index 934e8c46..5a9e7321 100644 --- a/app/workers/mail_worker.rb +++ b/app/workers/mail_worker.rb @@ -2,26 +2,13 @@ class MailWorker include Sidekiq::Worker sidekiq_options :retry => false - def perform(campaign_id, launch = true) - @campaign = Campaign.find_by_id(campaign_id) - @blast = @campaign.blasts.create(test: !launch) - @mailer = CampaignMailer.new(@campaign, @blast) - - if launch - if @mailer.valid? and @mailer.victims_valid? - @mailer.launch! - end - @blast.message = @mailer.messages - else - unless @mailer.valid? and not @mailer.test_victim_valid? - @blast.message = "#{@mailer.messages.join(". ")}" - end - if @mailer.test! - @blast.message = "#{@mailer.messages.join(". ")}" - else - @blast.message = "Email has failed" - end + def perform(campaign_id, target, blast_id, method) + logger.info "Attempting to send mail: campaign_id #{campaign_id}, target #{target}, blast_id #{blast_id}" + begin + PhishingFrenzyMailer.phish(campaign_id, target, blast_id, method) + rescue => e + logger.error "Failed to send mail: campaign_id #{campaign_id}, target #{target}, blast_id #{blast_id} - #{e.message}" + raise end - @blast.save end -end \ No newline at end of file +end diff --git a/config/application.rb b/config/application.rb index 00a1d9f2..8542fc9b 100644 --- a/config/application.rb +++ b/config/application.rb @@ -5,9 +5,6 @@ Bundler.require(:default, Rails.env) module PhishingFramework - # Default website for reports stuff, must change to callback. - SITE_URL = "http://pfadmin.local" - class Application < Rails::Application # Settings in config/environments/* take precedence over those specified here. diff --git a/config/database.yml b/config/database.yml index 6934ca86..883d612d 100644 --- a/config/database.yml +++ b/config/database.yml @@ -13,7 +13,7 @@ development: encoding: utf8 reconnect: false database: pf_dev - pool: 5 + pool: 25 username: pf_dev password: password host: localhost @@ -26,7 +26,7 @@ test: encoding: utf8 reconnect: false database: pf_test - pool: 5 + pool: 25 username: pf_test password: password host: localhost @@ -36,7 +36,7 @@ production: encoding: utf8 reconnect: false database: pf_prod - pool: 5 + pool: 25 username: pf_prod password: pf_prod host: localhost diff --git a/config/environments/development.rb b/config/environments/development.rb index b3235be1..43131dd4 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -37,4 +37,9 @@ config.assets.debug = true config.eager_load = false + + config.after_initialize do + Bullet.enable = true + Bullet.alert = true + end end diff --git a/config/routes.rb b/config/routes.rb index 4d9844e5..7df3cc15 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -32,6 +32,7 @@ get 'list' get 'aboutus' get 'victims' + get 'activity' delete 'destroy' end member do diff --git a/db/migrate/20150415015416_add_baits_count_to_blasts.rb b/db/migrate/20150415015416_add_baits_count_to_blasts.rb new file mode 100644 index 00000000..482c6224 --- /dev/null +++ b/db/migrate/20150415015416_add_baits_count_to_blasts.rb @@ -0,0 +1,15 @@ +class AddBaitsCountToBlasts < ActiveRecord::Migration + def self.up + add_column :blasts, :baits_count, :integer, default: 0 + + # assign counters for existing records + Blast.reset_column_information + Blast.all.each do |b| + Blast.update_counters b.id, :baits_count => b.baits.length + end + end + + def self.down + remove_column :blasts, :baits_count + end +end diff --git a/db/migrate/20150415021923_add_refresh_time_to_global_settings.rb b/db/migrate/20150415021923_add_refresh_time_to_global_settings.rb new file mode 100644 index 00000000..e3e47f04 --- /dev/null +++ b/db/migrate/20150415021923_add_refresh_time_to_global_settings.rb @@ -0,0 +1,5 @@ +class AddRefreshTimeToGlobalSettings < ActiveRecord::Migration + def change + add_column :global_settings, :reports_refresh, :integer, default: 15 + end +end diff --git a/db/migrate/20150515010202_create_activities.rb b/db/migrate/20150515010202_create_activities.rb new file mode 100644 index 00000000..b95c756d --- /dev/null +++ b/db/migrate/20150515010202_create_activities.rb @@ -0,0 +1,23 @@ +# Migration responsible for creating a table with activities +class CreateActivities < ActiveRecord::Migration + # Create table + def self.up + create_table :activities do |t| + t.belongs_to :trackable, :polymorphic => true + t.belongs_to :owner, :polymorphic => true + t.string :key + t.text :parameters + t.belongs_to :recipient, :polymorphic => true + + t.timestamps + end + + add_index :activities, [:trackable_id, :trackable_type] + add_index :activities, [:owner_id, :owner_type] + add_index :activities, [:recipient_id, :recipient_type] + end + # Drop table + def self.down + drop_table :activities + end +end diff --git a/db/migrate/20150515012820_add_admin_id_to_models.rb b/db/migrate/20150515012820_add_admin_id_to_models.rb new file mode 100644 index 00000000..ed1d38db --- /dev/null +++ b/db/migrate/20150515012820_add_admin_id_to_models.rb @@ -0,0 +1,6 @@ +class AddAdminIdToModels < ActiveRecord::Migration + def change + add_reference :campaigns, :admin, index: true, foreign_key: true + add_reference :templates, :admin, index: true, foreign_key: true + end +end diff --git a/db/migrate/20150714194319_add_replyto_to_email_settings.rb b/db/migrate/20150714194319_add_replyto_to_email_settings.rb new file mode 100644 index 00000000..4ba51e68 --- /dev/null +++ b/db/migrate/20150714194319_add_replyto_to_email_settings.rb @@ -0,0 +1,5 @@ +class AddReplytoToEmailSettings < ActiveRecord::Migration + def change + add_column :email_settings, :reply_to, :string + end +end diff --git a/db/migrate/20150718022848_change_default_asynchronous_value_in_global_settings.rb b/db/migrate/20150718022848_change_default_asynchronous_value_in_global_settings.rb new file mode 100644 index 00000000..e19954be --- /dev/null +++ b/db/migrate/20150718022848_change_default_asynchronous_value_in_global_settings.rb @@ -0,0 +1,6 @@ +class ChangeDefaultAsynchronousValueInGlobalSettings < ActiveRecord::Migration + def change + change_column :global_settings, :asynchronous, :boolean, default: true + GlobalSettings.update_all(asynchronous: true) + end +end diff --git a/db/migrate/20150718023513_add_site_url_to_global_settings.rb b/db/migrate/20150718023513_add_site_url_to_global_settings.rb new file mode 100644 index 00000000..fffce3bf --- /dev/null +++ b/db/migrate/20150718023513_add_site_url_to_global_settings.rb @@ -0,0 +1,5 @@ +class AddSiteUrlToGlobalSettings < ActiveRecord::Migration + def change + add_column :global_settings, :site_url, :string, default: 'https://phishingfrenzy.local' + end +end diff --git a/db/schema.rb b/db/schema.rb index 1de928d6..0cbc79ee 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,24 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20141111063547) do +ActiveRecord::Schema.define(version: 20150718023513) do + + create_table "activities", force: true do |t| + t.integer "trackable_id" + t.string "trackable_type" + t.integer "owner_id" + t.string "owner_type" + t.string "key" + t.text "parameters" + t.integer "recipient_id" + t.string "recipient_type" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "activities", ["owner_id", "owner_type"], name: "index_activities_on_owner_id_and_owner_type", using: :btree + add_index "activities", ["recipient_id", "recipient_type"], name: "index_activities_on_recipient_id_and_recipient_type", using: :btree + add_index "activities", ["trackable_id", "trackable_type"], name: "index_activities_on_trackable_id_and_trackable_type", using: :btree create_table "admins", force: true do |t| t.string "name" @@ -66,8 +83,9 @@ t.integer "number_of_targets" t.integer "emails_sent", default: 0 t.string "message", default: "Started " - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" + t.integer "baits_count", default: 0 end create_table "campaign_settings", force: true do |t| @@ -102,8 +120,10 @@ t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "test_email" + t.integer "admin_id" end + add_index "campaigns", ["admin_id"], name: "index_campaigns_on_admin_id", using: :btree add_index "campaigns", ["template_id"], name: "index_campaigns_on_template_id", using: :btree create_table "clones", force: true do |t| @@ -146,6 +166,7 @@ t.string "domain" t.string "authentication" t.boolean "enable_starttls_auto" + t.string "reply_to" end add_index "email_settings", ["campaign_id"], name: "index_email_settings_on_campaign_id", using: :btree @@ -157,11 +178,13 @@ t.datetime "updated_at", null: false t.string "command_apache_status" t.string "command_apache_vhosts", default: "apache2ctl -S" - t.boolean "asynchronous", default: false + t.boolean "asynchronous", default: true t.string "bing_api" t.string "beef_url" t.string "sites_enabled_path", default: "/etc/apache2/sites-enabled" t.string "beef_apikey" + t.integer "reports_refresh", default: 15 + t.string "site_url", default: "https://phishingfrenzy.local" end create_table "harvested_emails", force: true do |t| @@ -235,8 +258,10 @@ t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "directory_index" + t.integer "admin_id" end + add_index "templates", ["admin_id"], name: "index_templates_on_admin_id", using: :btree add_index "templates", ["campaign_id"], name: "index_templates_on_campaign_id", using: :btree create_table "versions", force: true do |t| diff --git a/public/uploads/archive_no_template_yaml.zip b/public/uploads/archive_no_template_yaml.zip old mode 100644 new mode 100755 diff --git a/public/uploads/intel_password_checker 2.zip b/public/uploads/intel_password_checker 2.zip old mode 100644 new mode 100755 diff --git a/public/uploads/uploads.txt b/public/uploads/uploads.txt old mode 100644 new mode 100755 diff --git a/spec/requests/campaigns_spec.rb b/spec/requests/campaigns_spec.rb index 9c3a532e..a05ddba3 100644 --- a/spec/requests/campaigns_spec.rb +++ b/spec/requests/campaigns_spec.rb @@ -7,7 +7,7 @@ describe "GET /" do it "request the dashboard of campaigns" do visit root_path - expect(page).to have_content("Dashboard") + expect(page).to have_content("Recent Activity") end it "clicks on the reports button for an existing campaign from the dashboard" do @@ -68,7 +68,6 @@ campaign = create(:campaign) visit campaign_path(campaign) fill_in("campaign_test_email", with: "user@phishingfrenzy.local") - fill_in("campaign_email_settings_attributes_smtp_server", with: "smtp.secureserver.net") fill_in("campaign_email_settings_attributes_smtp_server_out", with: "smtpout.secureserver.net") fill_in("campaign_email_settings_attributes_domain", with: "phishingfrenzy.local") fill_in("campaign_email_settings_attributes_smtp_username", with: "user@phishingfrenzy.local") @@ -81,11 +80,10 @@ fill_in("campaign_campaign_settings_attributes_fqdn", with: "sub.phishingfrenzy.local") click_on("Save Settings") expect(page).to have_selector("#campaign_test_email[value='user@phishingfrenzy.local']") - expect(page).to have_selector("#campaign_email_settings_attributes_smtp_server[value='smtp.secureserver.net']") expect(page).to have_selector("#campaign_email_settings_attributes_smtp_server_out[value='smtpout.secureserver.net']") expect(page).to have_selector("#campaign_email_settings_attributes_domain[value='phishingfrenzy.local']") expect(page).to have_selector("#campaign_email_settings_attributes_smtp_username[value='user@phishingfrenzy.local']") - expect(page).to have_selector("#campaign_email_settings_attributes_smtp_password[value='secretPasswd']") + expect(page).to_not have_selector("#campaign_email_settings_attributes_smtp_password[value='secretPasswd']") expect(page).to have_selector("#campaign_email_settings_attributes_smtp_port[value='3535']") expect(page).to have_selector("#campaign_email_settings_attributes_subject[value='Subject Line']") expect(page).to have_selector("#campaign_email_settings_attributes_from[value='user@phishingfrenzy.local']") @@ -163,7 +161,7 @@ select("Intel Password Checker", from: "campaign_template_id") click_on("Save Settings") click_on("Test") - expect(page).to have_content("Campaign test email sent") + expect(page).to have_content("Campaign test email queued for test") expect(page).to have_css("#recentBlasts > div > table > tbody > tr:nth-child(1)") end diff --git a/vendor/templates/efax/efax.html.erb b/vendor/templates/efax/efax.html.erb index 5a9efe28..04ebf5e8 100644 --- a/vendor/templates/efax/efax.html.erb +++ b/vendor/templates/efax/efax.html.erb @@ -9,7 +9,7 @@ You have received a 2 page fax at 2013-3-8 8:35:31 CST

    * Reference number for this fax is Fax_di23-208571567114-2013234

    -Click here to view the message
    +Click here to view the message


    Please visit eFax.com if you have any questions regarding this service.
    diff --git a/vendor/templates/intel/intel.html.erb b/vendor/templates/intel/intel.html.erb index 92c0ea4d..55ca9a43 100644 --- a/vendor/templates/intel/intel.html.erb +++ b/vendor/templates/intel/intel.html.erb @@ -44,7 +44,7 @@ face=Tahoma>Subject: Strong Password Checking Tool non-recording feature on Intels Website that helps determine your password's strength as you type.

    - Visit https://www-ssl.intel.com/content/www/us/en/forms/passwordwin.html + Visit https://www-ssl.intel.com/content/www/us/en/forms/passwordwin.html to test the strength of your passwords.

    Thank you,