From b164af65d19f0cd78afd2886e112129867e394a9 Mon Sep 17 00:00:00 2001 From: Marcos Ronaldo Date: Mon, 19 Oct 2015 11:32:21 -0200 Subject: [PATCH] API improvements: --- app/controllers/application_controller.rb | 8 +------- lib/find_by_contents.rb | 11 +++++++++++ lib/noosfero/api/api.rb | 11 +++++++++-- lib/noosfero/api/entities.rb | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------ lib/noosfero/api/helpers.rb | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------- lib/noosfero/api/session.rb | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------- lib/noosfero/api/v1/articles.rb | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------- lib/noosfero/api/v1/comments.rb | 4 ++-- lib/noosfero/api/v1/contacts.rb | 28 ++++++++++++++++++++++++++++ lib/noosfero/api/v1/environments.rb | 18 ++++++++++++++++++ lib/noosfero/api/v1/people.rb | 7 +++++++ lib/noosfero/api/v1/search.rb | 43 +++++++++++++++++++++++++++++++++++++++++++ lib/noosfero/api/v1/tags.rb | 30 ++++++++++++++++++++++++++++++ lib/noosfero/api/v1/users.rb | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/sanitize_params.rb | 41 +++++++++++++++++++++++++++++++++++++++++ test/unit/api/articles_test.rb | 251 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------- test/unit/api/categories_test.rb | 41 ++++++++++++++++++++++++++--------------- test/unit/api/comments_test.rb | 13 +++++++++++++ test/unit/api/communities_test.rb | 39 ++++++++++++++++++++------------------- test/unit/api/enterprises_test.rb | 32 ++++++++++++++++---------------- test/unit/api/helpers_test.rb | 42 +++++++++++++++++++++++++++++++++--------- test/unit/api/people_test.rb | 17 +++++++++++++++++ test/unit/api/search_test.rb | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ test/unit/api/session_test.rb | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ test/unit/api/test_helper.rb | 10 ++++++++-- test/unit/api/users_test.rb | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 26 files changed, 1392 insertions(+), 196 deletions(-) create mode 100644 lib/find_by_contents.rb create mode 100644 lib/noosfero/api/v1/contacts.rb create mode 100644 lib/noosfero/api/v1/environments.rb create mode 100644 lib/noosfero/api/v1/search.rb create mode 100644 lib/noosfero/api/v1/tags.rb create mode 100644 lib/noosfero/api/v1/users.rb create mode 100644 lib/sanitize_params.rb create mode 100644 test/unit/api/search_test.rb create mode 100644 test/unit/api/users_test.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index bca0d6e..e537c47 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -184,13 +184,7 @@ class ApplicationController < ActionController::Base end include SearchTermHelper - - def find_by_contents(asset, context, scope, query, paginate_options={:page => 1}, options={}) - scope = scope.with_templates(options[:template_id]) unless options[:template_id].blank? - search = plugins.dispatch_first(:find_by_contents, asset, scope, query, paginate_options, options) - register_search_term(query, scope.count, search[:results].count, context, asset) - search - end + include FindByContents def find_suggestions(query, context, asset, options={}) plugins.dispatch_first(:find_suggestions, query, context, asset, options) diff --git a/lib/find_by_contents.rb b/lib/find_by_contents.rb new file mode 100644 index 0000000..064e570 --- /dev/null +++ b/lib/find_by_contents.rb @@ -0,0 +1,11 @@ +module FindByContents + + def find_by_contents(asset, context, scope, query, paginate_options={:page => 1}, options={}) + scope = scope.with_templates(options[:template_id]) unless options[:template_id].blank? + search = plugins.dispatch_first(:find_by_contents, asset, scope, query, paginate_options, options) + register_search_term(query, scope.count, search[:results].count, context, asset) + search + end + +end + diff --git a/lib/noosfero/api/api.rb b/lib/noosfero/api/api.rb index e1eefad..621be69 100644 --- a/lib/noosfero/api/api.rb +++ b/lib/noosfero/api/api.rb @@ -1,7 +1,7 @@ require 'grape' #require 'rack/contrib' - Dir["#{Rails.root}/lib/noosfero/api/*.rb"].each {|file| require file unless file =~ /api\.rb/} + module Noosfero module API class API < Grape::API @@ -17,7 +17,6 @@ module Noosfero end @@NOOSFERO_CONF = nil - def self.NOOSFERO_CONF if @@NOOSFERO_CONF @@NOOSFERO_CONF @@ -27,9 +26,11 @@ module Noosfero end end + before { set_locale } before { setup_multitenancy } before { detect_stuff_by_domain } before { filter_disabled_plugins_endpoints } + before { init_noosfero_plugins } after { set_session_cookie } version 'v1' @@ -41,11 +42,17 @@ module Noosfero mount V1::Articles mount V1::Comments + mount V1::Users mount V1::Communities mount V1::People mount V1::Enterprises mount V1::Categories mount V1::Tasks + mount V1::Tags + mount V1::Environments + mount V1::Search + mount V1::Contacts + mount Session # hook point which allow plugins to add Grape::API extensions to API::API diff --git a/lib/noosfero/api/entities.rb b/lib/noosfero/api/entities.rb index b892e39..28394e8 100644 --- a/lib/noosfero/api/entities.rb +++ b/lib/noosfero/api/entities.rb @@ -6,6 +6,17 @@ module Noosfero date.strftime('%Y/%m/%d %H:%M:%S') if date end + def self.can_display? profile, options, field, admin_only = false + current = options[:current_person] + admin = !current.blank? && current.is_admin? + owner = !current.blank? && current == profile + public_field = profile.public_fields.include? field.to_s + friend = !current.blank? && current.friends.include?(profile) + + return true if admin + return !admin_only && (owner||friend||public_field) + end + class Image < Entity root 'images', 'image' @@ -30,66 +41,81 @@ module Noosfero end end + class CategoryBase < Entity + root 'categories', 'category' + expose :name, :id, :slug + end + + class Category < CategoryBase + root 'categories', 'category' + expose :full_name do |category, options| + category.full_name + end + expose :parent, :using => CategoryBase, if: { parent: true } + expose :children, :using => CategoryBase, if: { children: true } + expose :image, :using => Image + expose :display_color + end + + class Region < Category + root 'regions', 'region' + expose :parent_id + end + class Profile < Entity expose :identifier, :name, :id expose :created_at, :format_with => :timestamp expose :updated_at, :format_with => :timestamp expose :image, :using => Image + expose :region, :using => Region end - class User < Entity + class UserBasic < Entity expose :id expose :login end class Person < Profile root 'people', 'person' - expose :user, :using => User + expose :user, :using => UserBasic, documentation: {type: 'User', desc: 'The user data of a person' } end + class Enterprise < Profile root 'enterprises', 'enterprise' end + class Community < Profile root 'communities', 'community' expose :description - expose :categories - expose :members, :using => Person - end - - class CategoryBase < Entity - root 'categories', 'category' - expose :name, :id - end - - class Category < CategoryBase - root 'categories', 'category' - expose :slug - expose :full_name do |category, options| - category.full_name + expose :admins do |community, options| + community.admins.map{|admin| {"name"=>admin.name, "id"=>admin.id}} end - expose :parent, :using => CategoryBase, if: { parent: true } - expose :children, :using => CategoryBase, if: { children: true } - expose :image, :using => Image + expose :categories, :using => Category + expose :members, :using => Person end class ArticleBase < Entity root 'articles', 'article' expose :id expose :body - expose :abstract + expose :abstract, documentation: {type: 'String', desc: 'Teaser of the body'} expose :created_at, :format_with => :timestamp expose :updated_at, :format_with => :timestamp expose :title, :documentation => {:type => "String", :desc => "Title of the article"} - expose :created_by, :as => :author, :using => Profile - expose :profile, :using => Profile + expose :created_by, :as => :author, :using => Profile, :documentation => {type: 'Profile', desc: 'The profile author that create the article'} + expose :profile, :using => Profile, :documentation => {type: 'Profile', desc: 'The profile associated with the article'} expose :categories, :using => Category expose :image, :using => Image - #TODO Apply vote stuff in core and make this test expose :votes_for expose :votes_against expose :setting expose :position expose :hits + expose :start_date + expose :end_date, :documentation => {type: 'DateTime', desc: 'The date of finish of the article'} + expose :tag_list + expose :children_count + expose :slug, :documentation => {:type => "String", :desc => "Trimmed and parsed name of a article"} expose :path end @@ -106,8 +132,31 @@ module Noosfero expose :author, :using => Profile end + class User < Entity + root 'users', 'user' + + attrs = [:id,:login,:email,:activated?] + aliases = {:activated? => :activated} + + attrs.each do |attribute| + name = aliases.has_key?(attribute) ? aliases[attribute] : attribute + expose attribute, :as => name, :if => lambda{|user,options| Entities.can_display?(user.person, options, attribute)} + end + + expose :person, :using => Person + expose :permissions, :if => lambda{|user,options| Entities.can_display?(user.person, options, :permissions, true)} do |user, options| + output = {} + user.person.role_assignments.map do |role_assigment| + if role_assigment.resource.respond_to?(:identifier) && !role_assigment.role.nil? + output[role_assigment.resource.identifier] = role_assigment.role.permissions + end + end + output + end + end + class UserLogin < User - expose :private_token + expose :private_token, documentation: {type: 'String', desc: 'A valid authentication code for post/delete api actions'} end class Task < Entity @@ -116,6 +165,16 @@ module Noosfero expose :type end + class Environment < Entity + expose :name + end + + class Tag < Entity + root 'tags', 'tag' + expose :name + end + + end end end diff --git a/lib/noosfero/api/helpers.rb b/lib/noosfero/api/helpers.rb index 7ece260..5da1795 100644 --- a/lib/noosfero/api/helpers.rb +++ b/lib/noosfero/api/helpers.rb @@ -1,8 +1,24 @@ -module Noosfero - module API - module APIHelpers +require 'grape' +require_relative '../../find_by_contents' + + module Noosfero; + module API + module APIHelpers PRIVATE_TOKEN_PARAM = :private_token - DEFAULT_ALLOWED_PARAMETERS = [:parent_id, :from, :until, :content_type] + DEFAULT_ALLOWED_PARAMETERS = [:parent_id, :from, :until, :content_type, :author_id] + + include SanitizeParams + include Noosfero::Plugin::HotSpot + include ForgotPasswordHelper + include SearchTermHelper + + def set_locale + I18n.locale = (params[:lang] || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en') + end + + def init_noosfero_plugins + plugins + end def current_user private_token = (params[PRIVATE_TOKEN_PARAM] || headers['Private-Token']).to_s @@ -23,6 +39,22 @@ module Noosfero @environment end + include FindByContents + + #################################################################### + #### SEARCH + #################################################################### + def multiple_search?(searches=nil) + ['index', 'category_index'].include?(params[:action]) || (searches && searches.size > 1) + end + #################################################################### + + def logger + logger = Logger.new(File.join(Rails.root, 'log', "#{ENV['RAILS_ENV'] || 'production'}_api.log")) + logger.formatter = GrapeLogging::Formatters::Default.new + logger + end + def limit limit = params[:limit].to_i limit = default_limit if limit <= 0 @@ -50,7 +82,7 @@ module Noosfero def find_article(articles, id) article = articles.find(id) - article.display_to?(current_user.person) ? article : forbidden! + article.display_to?(current_person) ? article : forbidden! end def post_article(asset, params) @@ -75,12 +107,25 @@ module Noosfero present article, :with => Entities::Article, :fields => params[:fields] end - def present_articles(asset) - articles = select_filtered_collection_of(asset, 'articles', params) - articles = articles.display_filter(current_person, nil) + def present_articles_for_asset(asset, method = 'articles') + articles = find_articles(asset, method) + present_articles(articles) + end + + def present_articles(articles) present articles, :with => Entities::Article, :fields => params[:fields] end + def find_articles(asset, method = 'articles') + articles = select_filtered_collection_of(asset, method, params) + if current_person.present? + articles = articles.display_filter(current_person, nil) + else + articles = articles.published + end + articles + end + def find_task(asset, id) task = asset.tasks.find(id) current_person.has_permission?(task.permission, asset) ? task : forbidden! @@ -127,8 +172,27 @@ module Noosfero conditions end - def make_order_with_parameters(params) - params[:order] || "created_at DESC" + # changing make_order_with_parameters to avoid sql injection + def make_order_with_parameters(object, method, params) + order = "created_at DESC" + unless params[:order].blank? + if params[:order].include? '\'' or params[:order].include? '"' + order = "created_at DESC" + elsif ['RANDOM()', 'RANDOM'].include? params[:order].upcase + order = 'RANDOM()' + else + field_name, direction = params[:order].split(' ') + assoc = object.class.reflect_on_association(method.to_sym) + if !field_name.blank? and assoc + if assoc.klass.attribute_names.include? field_name + if direction.present? and ['ASC','DESC'].include? direction.upcase + order = "#{field_name} #{direction.upcase}" + end + end + end + end + end + return order end def make_page_number_with_parameters(params) @@ -152,25 +216,36 @@ module Noosfero end def by_reference(scope, params) - if params[:reference_id] - created_at = scope.find(params[:reference_id]).created_at - scope.send("#{params.key?(:oldest) ? 'older_than' : 'younger_than'}", created_at) + reference_id = params[:reference_id].to_i == 0 ? nil : params[:reference_id].to_i + if reference_id.nil? + scope else + created_at = scope.find(reference_id).created_at + scope.send("#{params.key?(:oldest) ? 'older_than' : 'younger_than'}", created_at) + end + end + + def by_categories(scope, params) + category_ids = params[:category_ids] + if category_ids.nil? scope + else + scope.joins(:categories).where(:categories => {:id => category_ids}) end end def select_filtered_collection_of(object, method, params) conditions = make_conditions_with_parameter(params) - order = make_order_with_parameters(params) + order = make_order_with_parameters(object,method,params) page_number = make_page_number_with_parameters(params) per_page = make_per_page_with_parameters(params) timestamp = make_timestamp_with_parameters_and_method(params, method) objects = object.send(method) objects = by_reference(objects, params) + objects = by_categories(objects, params) - objects = objects.where(conditions).where(timestamp).page(page_number).per_page(per_page).order(order) + objects = objects.where(conditions).where(timestamp).page(page_number).per_page(per_page).reorder(order) objects end @@ -179,6 +254,7 @@ module Noosfero unauthorized! unless current_user end + # Checks the occurrences of uniqueness of attributes, each attribute must be present in the params hash # or a Bad Request error is invoked. # @@ -198,20 +274,6 @@ module Noosfero attrs end - def verify_recaptcha_v2(remote_ip, g_recaptcha_response, private_key, api_recaptcha_verify_uri) - verify_hash = { - "secret" => private_key, - "remoteip" => remote_ip, - "response" => g_recaptcha_response - } - uri = URI(api_recaptcha_verify_uri) - https = Net::HTTP.new(uri.host, uri.port) - https.use_ssl = true - request = Net::HTTP::Post.new(uri.path) - request.set_form_data(verify_hash) - JSON.parse(https.request(request).body) - end - ########################################## # error helpers # ########################################## @@ -247,13 +309,22 @@ module Noosfero render_api_error!(_('Method Not Allowed'), 405) end - def render_api_error!(message, status) - error!({'message' => message, :code => status}, status) + # javascript_console_message is supposed to be executed as console.log() + def render_api_error!(user_message, status, log_message = nil, javascript_console_message = nil) + message_hash = {'message' => user_message, :code => status} + message_hash[:javascript_console_message] = javascript_console_message if javascript_console_message.present? + log_msg = "#{status}, User message: #{user_message}" + log_msg = "#{log_message}, #{log_msg}" if log_message.present? + log_msg = "#{log_msg}, Javascript Console Message: #{javascript_console_message}" if javascript_console_message.present? + logger.error log_msg unless Rails.env.test? + error!(message_hash, status) end def render_api_errors!(messages) + messages = messages.to_a if messages.class == ActiveModel::Errors render_api_error!(messages.join(','), 400) end + protected def set_session_cookie @@ -278,7 +349,7 @@ module Noosfero end def filter_disabled_plugins_endpoints - not_found! if Noosfero::API::API.endpoint_unavailable?(self, !@environment) + not_found! if Noosfero::API::API.endpoint_unavailable?(self, @environment) end private @@ -305,7 +376,6 @@ module Noosfero def period(from_date, until_date) begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date end_period = until_date.nil? ? DateTime.now : until_date - begin_period..end_period end diff --git a/lib/noosfero/api/session.rb b/lib/noosfero/api/session.rb index 030962a..d995f9e 100644 --- a/lib/noosfero/api/session.rb +++ b/lib/noosfero/api/session.rb @@ -2,7 +2,6 @@ require "uri" module Noosfero module API - class Session < Grape::API # Login to get token @@ -14,11 +13,15 @@ module Noosfero # Example Request: # POST http://localhost:3000/api/v1/login?login=adminuser&password=admin post "/login" do - user ||= User.authenticate(params[:login], params[:password], environment) + begin + user ||= User.authenticate(params[:login], params[:password], environment) + rescue NoosferoExceptions::UserNotActivated => e + render_api_error!(e.message, 401) + end return unauthorized! unless user @current_user = user - present user, :with => Entities::UserLogin + present user, :with => Entities::UserLogin, :current_person => current_person end # Create user. @@ -28,34 +31,103 @@ module Noosfero # password (required) - Password # login - login # Example Request: - # POST /register?email=some@mail.com&password=pas&login=some + # POST /register?email=some@mail.com&password=pas&password_confirmation=pas&login=some params do requires :email, type: String, desc: _("Email") requires :login, type: String, desc: _("Login") requires :password, type: String, desc: _("Password") end + post "/register" do - unique_attributes! User, [:email, :login] - attrs = attributes_for_keys [:email, :login, :password] - attrs[:password_confirmation] = attrs[:password] - - #Commented for stress tests - - # remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR']) - # private_key = API.NOOSFERO_CONF['api_recaptcha_private_key'] - # api_recaptcha_verify_uri = API.NOOSFERO_CONF['api_recaptcha_verify_uri'] - # captcha_result = verify_recaptcha_v2(remote_ip, params['g-recaptcha-response'], private_key, api_recaptcha_verify_uri) - user = User.new(attrs) -# if captcha_result["success"] and user.save - if user.save - user.activate - user.generate_private_token! - present user, :with => Entities::UserLogin - else - message = user.errors.to_json + attrs = attributes_for_keys [:email, :login, :password, :password_confirmation] + environment.signup_person_fields + name = params[:name].present? ? params[:name] : attrs[:email] + attrs[:password_confirmation] = attrs[:password] if !attrs.has_key?(:password_confirmation) + user = User.new(attrs.merge(:name => name)) + + begin + user.signup! + user.generate_private_token! if user.activated? + present user, :with => Entities::UserLogin, :current_person => current_person + rescue ActiveRecord::RecordInvalid + message = user.errors.as_json.merge((user.person.present? ? user.person.errors : {}).as_json).to_json render_api_error!(message, 400) end end + + params do + requires :activation_code, type: String, desc: _("Activation token") + end + + # Activate a user. + # + # Parameter: + # activation_code (required) - Activation token + # Example Request: + # PATCH /activate?activation_code=28259abd12cc6a64ef9399cf3286cb998b96aeaf + patch "/activate" do + user = User.find_by_activation_code(params[:activation_code]) + if user + unless user.environment.enabled?('admin_must_approve_new_users') + if user.activate + user.generate_private_token! + present user, :with => Entities::UserLogin, :current_person => current_person + end + else + if user.create_moderate_task + user.activation_code = nil + user.save! + + # Waiting for admin moderate user registration + status 202 + body({ + :message => 'Waiting for admin moderate user registration' + }) + end + end + else + # Token not found in database + render_api_error!(_('Token is invalid'), 412) + end + end + + # Request a new password. + # + # Parameters: + # value (required) - Email or login + # Example Request: + # POST /forgot_password?value=some@mail.com + post "/forgot_password" do + requestors = fetch_requestors(params[:value]) + not_found! if requestors.blank? + remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR']) + requestors.each do |requestor| + ChangePassword.create!(:requestor => requestor) + end + end + + params do + requires :code, type: String, desc: _("Forgot password code") + end + # Change password + # + # Parameters: + # code (required) - Change password code + # password (required) + # password_confirmation (required) + # Example Request: + # PATCH /new_password?code=xxxx&password=secret&password_confirmation=secret + patch "/new_password" do + change_password = ChangePassword.find_by_code(params[:code]) + not_found! if change_password.nil? + + if change_password.update_attributes(:password => params[:password], :password_confirmation => params[:password_confirmation]) + change_password.finish + present change_password.requestor.user, :with => Entities::UserLogin, :current_person => current_person + else + something_wrong! + end + end + end end end diff --git a/lib/noosfero/api/v1/articles.rb b/lib/noosfero/api/v1/articles.rb index bb6a6a7..c8f3e82 100644 --- a/lib/noosfero/api/v1/articles.rb +++ b/lib/noosfero/api/v1/articles.rb @@ -2,7 +2,6 @@ module Noosfero module API module V1 class Articles < Grape::API - before { authenticate! } ARTICLE_TYPES = Article.descendants.map{|a| a.to_s} @@ -17,39 +16,148 @@ module Noosfero # # Example Request: # GET host/api/v1/articles?from=2013-04-04-14:41:43&until=2015-04-04-14:41:43&limit=10&private_token=e96fff37c2238fdab074d1dcea8e6317 + + desc 'Return all articles of all kinds' do + detail 'Get all articles filtered by fields in query params' + params Noosfero::API::Entities::Article.documentation + success Noosfero::API::Entities::Article + failure [[403, 'Forbidden']] + named 'ArticlesList' + headers [ + 'Per-Page' => { + description: 'Total number of records', + required: false + } + ] + end get do - present_articles(environment) + present_articles_for_asset(environment) end - desc "Return the article id" - get ':id' do + desc "Return one article by id" do + detail 'Get only one article by id. If not found the "forbidden" http error is showed' + params Noosfero::API::Entities::Article.documentation + success Noosfero::API::Entities::Article + failure [[403, 'Forbidden']] + named 'ArticleById' + end + get ':id', requirements: {id: /[0-9]+/} do present_article(environment) end + post ':id' do + article = environment.articles.find(params[:id]) + return forbidden! unless article.allow_edit?(current_person) + article.update_attributes!(params[:article]) + present article, :with => Entities::Article, :fields => params[:fields] + end + + desc 'Report a abuse and/or violent content in a article by id' do + detail 'Submit a abuse (in general, a content violation) report about a specific article' + params Noosfero::API::Entities::Article.documentation + failure [[400, 'Bad Request']] + named 'ArticleReportAbuse' + end + post ':id/report_abuse' do + article = find_article(environment.articles, params[:id]) + profile = article.profile + begin + abuse_report = AbuseReport.new(:reason => params[:report_abuse]) + if !params[:content_type].blank? + article = params[:content_type].constantize.find(params[:content_id]) + abuse_report.content = article_reported_version(article) + end + + current_person.register_report(abuse_report, profile) + + if !params[:content_type].blank? + abuse_report = AbuseReport.find_by_reporter_id_and_abuse_complaint_id(current_person.id, profile.opened_abuse_complaint.id) + Delayed::Job.enqueue DownloadReportedImagesJob.new(abuse_report, article) + end + + { + :success => true, + :message => _('Your abuse report was registered. The administrators are reviewing your report.'), + } + rescue Exception => exception + #logger.error(exception.to_s) + render_api_error!(_('Your report couldn\'t be saved due to some problem. Please contact the administrator.'), 400) + end + + end + + desc "Returns the articles I voted" do + detail 'Get the Articles I make a vote' + failure [[403, 'Forbidden']] + named 'ArticleFollowers' + end + #FIXME refactor this method + get 'voted_by_me' do + present_articles(current_person.votes.collect(&:voteable)) + end + + desc 'Perform a vote on a article by id' do + detail 'Vote on a specific article with values: 1 (if you like) or -1 (if not)' + params Noosfero::API::Entities::UserLogin.documentation + failure [[401,'Unauthorized']] + named 'ArticleVote' + end + post ':id/vote' do + authenticate! + value = (params[:value] || 1).to_i + # FIXME verify allowed values + render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value) + article = find_article(environment.articles, params[:id]) + vote = Vote.new(:voteable => article, :voter => current_person, :vote => value) + {:vote => vote.save} + end + + desc 'Return the children of a article identified by id' do + detail 'Get all children articles of a specific article' + params Noosfero::API::Entities::Article.documentation + failure [[403, 'Forbidden']] + named 'ArticleChildren' + end + get ':id/children' do article = find_article(environment.articles, params[:id]) #TODO make tests for this situation votes_order = params.delete(:order) if params[:order]=='votes_score' articles = select_filtered_collection_of(article, 'children', params) - articles = articles.display_filter(current_person, nil) - + articles = articles.display_filter(current_person, article.profile) #TODO make tests for this situation if votes_order articles = articles.joins('left join votes on articles.id=votes.voteable_id').group('articles.id').reorder('sum(coalesce(votes.vote, 0)) DESC') end - Article.hit(articles) - present articles, :with => Entities::Article, :fields => params[:fields] + present_articles(articles) end + desc 'Return one child of a article identified by id' do + detail 'Get a child of a specific article' + params Noosfero::API::Entities::Article.documentation + success Noosfero::API::Entities::Article + failure [[403, 'Forbidden']] + named 'ArticleChild' + end get ':id/children/:child_id' do article = find_article(environment.articles, params[:id]) - present find_article(article.children, params[:child_id]), :with => Entities::Article, :fields => params[:fields] + child = find_article(article.children, params[:child_id]) + child.hit + present child, :with => Entities::Article, :fields => params[:fields] end + desc 'Suggest a article to another profile' do + detail 'Suggest a article to another profile (person, community...)' + params Noosfero::API::Entities::Article.documentation + success Noosfero::API::Entities::Task + failure [[401,'Unauthorized']] + named 'ArticleSuggest' + end post ':id/children/suggest' do + authenticate! parent_article = environment.articles.find(params[:id]) suggest_article = SuggestArticle.new @@ -66,8 +174,15 @@ module Noosfero # Example Request: # POST api/v1/articles/:id/children?private_token=234298743290432&article[name]=title&article[body]=body + desc 'Add a child article to a parent identified by id' do + detail 'Create a new article and associate to a parent' + params Noosfero::API::Entities::Article.documentation + success Noosfero::API::Entities::Article + failure [[401,'Unauthorized']] + named 'ArticleAddChild' + end post ':id/children' do - + authenticate! parent_article = environment.articles.find(params[:id]) return forbidden! unless parent_article.allow_create?(current_person) @@ -95,11 +210,37 @@ module Noosfero resource kind.pluralize.to_sym do segment "/:#{kind}_id" do resource :articles do + + desc "Return all articles associate with a profile of type #{kind}" do + detail 'Get a list of articles of a profile' + params Noosfero::API::Entities::Article.documentation + success Noosfero::API::Entities::Article + failure [[403, 'Forbidden']] + named 'ArticlesOfProfile' + end get do profile = environment.send(kind.pluralize).find(params["#{kind}_id"]) - present_articles(profile) + + if params[:path].present? + article = profile.articles.find_by_path(params[:path]) + if !article || !article.display_to?(current_person) + article = forbidden! + end + + present article, :with => Entities::Article, :fields => params[:fields] + else + + present_articles_for_asset(profile) + end end + desc "Return a article associate with a profile of type #{kind}" do + detail 'Get only one article of a profile' + params Noosfero::API::Entities::Article.documentation + success Noosfero::API::Entities::Article + failure [[403, 'Forbidden']] + named 'ArticleOfProfile' + end get ':id' do profile = environment.send(kind.pluralize).find(params["#{kind}_id"]) present_article(profile) @@ -107,6 +248,13 @@ module Noosfero # Example Request: # POST api/v1/{people,communities,enterprises}/:asset_id/articles?private_token=234298743290432&article[name]=title&article[body]=body + desc "Add a new article associated with a profile of type #{kind}" do + detail 'Create a new article and associate with a profile' + params Noosfero::API::Entities::Article.documentation + success Noosfero::API::Entities::Article + failure [[403, 'Forbidden']] + named 'ArticleCreateToProfile' + end post do profile = environment.send(kind.pluralize).find(params["#{kind}_id"]) post_article(profile, params) diff --git a/lib/noosfero/api/v1/comments.rb b/lib/noosfero/api/v1/comments.rb index d38ac81..edbcba8 100644 --- a/lib/noosfero/api/v1/comments.rb +++ b/lib/noosfero/api/v1/comments.rb @@ -30,8 +30,8 @@ module Noosfero # POST api/v1/articles/12/comments?private_token=2298743290432&body=new comment&title=New post ":id/comments" do article = find_article(environment.articles, params[:id]) - options = params.select { |key,v| !['id','private_token'].include?(key) }.merge(:author => current_person) - present article.comments.create(options), :with => Entities::Comment + options = params.select { |key,v| !['id','private_token'].include?(key) }.merge(:author => current_person, :source => article) + present Comment.create(options), :with => Entities::Comment end end diff --git a/lib/noosfero/api/v1/contacts.rb b/lib/noosfero/api/v1/contacts.rb new file mode 100644 index 0000000..a3b046a --- /dev/null +++ b/lib/noosfero/api/v1/contacts.rb @@ -0,0 +1,28 @@ +module Noosfero + module API + module V1 + class Contacts < Grape::API + + resource :communities do + + resource ':id/contact' do + #contact => {:name => 'some name', :email => 'test@mail.com', :subject => 'some title', :message => 'some message'} + desc "Send a contact message" + post do + profile = environment.communities.find(params[:id]) + forbidden! unless profile.present? + contact = Contact.new params[:contact].merge(dest: profile) + if contact.deliver + {:success => true} + else + {:success => false} + end + end + + end + end + + end + end + end +end diff --git a/lib/noosfero/api/v1/environments.rb b/lib/noosfero/api/v1/environments.rb new file mode 100644 index 0000000..909deea --- /dev/null +++ b/lib/noosfero/api/v1/environments.rb @@ -0,0 +1,18 @@ +module Noosfero + module API + module V1 + class Environments < Grape::API + + resource :environment do + + desc "Return the person information" + get '/signup_person_fields' do + present environment.signup_person_fields + end + + end + + end + end + end +end diff --git a/lib/noosfero/api/v1/people.rb b/lib/noosfero/api/v1/people.rb index dc9e8ab..bf13ea1 100644 --- a/lib/noosfero/api/v1/people.rb +++ b/lib/noosfero/api/v1/people.rb @@ -48,6 +48,13 @@ module Noosfero present person, :with => Entities::Person end + desc "Update person information" + post ':id' do + return forbidden! if current_person.id.to_s != params[:id] + current_person.update_attributes!(params[:person]) + present current_person, :with => Entities::Person + end + # Example Request: # POST api/v1/people?person[login]=some_login&person[password]=some_password&person[name]=Jack desc "Create person" diff --git a/lib/noosfero/api/v1/search.rb b/lib/noosfero/api/v1/search.rb new file mode 100644 index 0000000..e678254 --- /dev/null +++ b/lib/noosfero/api/v1/search.rb @@ -0,0 +1,43 @@ +module Noosfero + module API + module V1 + class Search < Grape::API + + resource :search do + resource :article do + get do + # Security checks + sanitize_params_hash(params) + # APIHelpers + asset = :articles + context = environment + + profile = environment.profiles.find(params[:profile_id]) if params[:profile_id] + scope = profile.nil? ? environment.articles.public : profile.articles.public + scope = scope.where(:type => params[:type]) if params[:type] && !(params[:type] == 'Article') + scope = scope.where(:parent_id => params[:parent_id]) if params[:parent_id].present? + scope = scope.joins(:categories).where(:categories => {:id => params[:category_ids]}) if params[:category_ids].present? + query = params[:query] || "" + order = "more_recent" + + options = {:filter => order, :template_id => params[:template_id]} + + paginate_options = params.select{|k,v| [:page, :per_page].include?(k.to_sym)} + paginate_options.each_pair{|k,v| v=v.to_i} + paginate_options[:page]=1 if !paginate_options.keys.include?(:page) + + search_result = find_by_contents(asset, context, scope, query, paginate_options.symbolize_keys, options) + + articles = search_result[:results] + + result = present_articles(articles) + + result + end + end + end + + end + end + end +end diff --git a/lib/noosfero/api/v1/tags.rb b/lib/noosfero/api/v1/tags.rb new file mode 100644 index 0000000..4bc0b7e --- /dev/null +++ b/lib/noosfero/api/v1/tags.rb @@ -0,0 +1,30 @@ +module Noosfero + module API + module V1 + class Tags < Grape::API + before { authenticate! } + + resource :articles do + + resource ':id/tags' do + + get do + article = find_article(environment.articles, params[:id]) + present article.tag_list + end + + desc "Add a tag to an article" + post do + article = find_article(environment.articles, params[:id]) + article.tag_list=params[:tags] + article.save + present article.tag_list + end + + end + end + + end + end + end +end diff --git a/lib/noosfero/api/v1/users.rb b/lib/noosfero/api/v1/users.rb new file mode 100644 index 0000000..9f2639f --- /dev/null +++ b/lib/noosfero/api/v1/users.rb @@ -0,0 +1,56 @@ +module Noosfero + module API + module V1 + class Users < Grape::API + before { authenticate! } + + resource :users do + + get do + users = select_filtered_collection_of(environment, 'users', params) + users = users.select{|u| u.person.display_info_to? current_person} + present users, :with => Entities::User, :current_person => current_person + end + + # Example Request: + # POST api/v1/users?user[login]=some_login&user[password]=some + post do + user = User.new(params[:user]) + user.terms_of_use = environment.terms_of_use + user.environment = environment + if !user.save + render_api_errors!(user.errors.full_messages) + end + + present user, :with => Entities::User, :current_person => current_person + end + + get "/me" do + present current_user, :with => Entities::User, :current_person => current_person + end + + get ":id" do + user = environment.users.find_by_id(params[:id]) + unless user.person.display_info_to? current_person + unauthorized! + end + present user, :with => Entities::User, :current_person => current_person + end + + get ":id/permissions" do + user = environment.users.find(params[:id]) + output = {} + user.person.role_assignments.map do |role_assigment| + if role_assigment.resource.respond_to?(:identifier) && role_assigment.resource.identifier == params[:profile] + output[:permissions] = role_assigment.role.permissions + end + end + present output + end + + end + + end + end + end +end diff --git a/lib/sanitize_params.rb b/lib/sanitize_params.rb new file mode 100644 index 0000000..676bc6a --- /dev/null +++ b/lib/sanitize_params.rb @@ -0,0 +1,41 @@ +module SanitizeParams + + protected + + # Check each request parameter for + # improper HTML or Script tags + def sanitize_params + sanitize_params_hash(request.params) + end + + # Given a params list sanitize all + def sanitize_params_hash(params) + params.each { |k, v| + if v.is_a?(String) + params[k] = sanitize_param v + elsif v.is_a?(Array) + params[k] = sanitize_array v + elsif v.kind_of?(Hash) + params[k] = sanitize_params_hash(v) + end + } + end + + # If the parameter was an array, + # try to sanitize each element in the array + def sanitize_array(array) + array.map! { |e| + if e.is_a?(String) + sanitize_param e + end + } + return array + end + + # Santitize a single value + def sanitize_param(value) + allowed_tags = %w(a acronym b strong i em li ul ol h1 h2 h3 h4 h5 h6 blockquote br cite sub sup ins p) + ActionController::Base.helpers.sanitize(value, tags: allowed_tags, attributes: %w(href title)) + end + +end diff --git a/test/unit/api/articles_test.rb b/test/unit/api/articles_test.rb index 3e9e58f..280fb8d 100644 --- a/test/unit/api/articles_test.rb +++ b/test/unit/api/articles_test.rb @@ -31,7 +31,7 @@ class ArticlesTest < ActiveSupport::TestCase end should 'not return article if user has no permission to view it' do - person = fast_create(Person) + person = fast_create(Person, :environment_id => environment.id) article = fast_create(Article, :profile_id => person.id, :name => "Some thing", :published => false) assert !article.published? @@ -48,8 +48,17 @@ class ArticlesTest < ActiveSupport::TestCase assert_equivalent [child1.id, child2.id], json["articles"].map { |a| a["id"] } end + should 'list public article children for not logged in access' do + article = fast_create(Article, :profile_id => user.person.id, :name => "Some thing") + child1 = fast_create(Article, :parent_id => article.id, :profile_id => user.person.id, :name => "Some thing") + child2 = fast_create(Article, :parent_id => article.id, :profile_id => user.person.id, :name => "Some thing") + get "/api/v1/articles/#{article.id}/children" + json = JSON.parse(last_response.body) + assert_equivalent [child1.id, child2.id], json["articles"].map { |a| a["id"] } + end + should 'not list children of forbidden article' do - person = fast_create(Person) + person = fast_create(Person, :environment_id => environment.id) article = fast_create(Article, :profile_id => person.id, :name => "Some thing", :published => false) child1 = fast_create(Article, :parent_id => article.id, :profile_id => person.id, :name => "Some thing") child2 = fast_create(Article, :parent_id => article.id, :profile_id => person.id, :name => "Some thing") @@ -58,7 +67,7 @@ class ArticlesTest < ActiveSupport::TestCase end should 'not return child of forbidden article' do - person = fast_create(Person) + person = fast_create(Person, :environment_id => environment.id) article = fast_create(Article, :profile_id => person.id, :name => "Some thing", :published => false) child = fast_create(Article, :parent_id => article.id, :profile_id => person.id, :name => "Some thing") get "/api/v1/articles/#{article.id}/children/#{child.id}?#{params.to_query}" @@ -66,7 +75,7 @@ class ArticlesTest < ActiveSupport::TestCase end should 'not return private child' do - person = fast_create(Person) + person = fast_create(Person, :environment_id => environment.id) article = fast_create(Article, :profile_id => person.id, :name => "Some thing") child = fast_create(Article, :parent_id => article.id, :profile_id => person.id, :name => "Some thing", :published => false) get "/api/v1/articles/#{article.id}/children/#{child.id}?#{params.to_query}" @@ -74,7 +83,7 @@ class ArticlesTest < ActiveSupport::TestCase end should 'not list private child' do - person = fast_create(Person) + person = fast_create(Person, :environment_id => environment.id) article = fast_create(Article, :profile_id => person.id, :name => "Some thing") child = fast_create(Article, :parent_id => article.id, :profile_id => person.id, :name => "Some thing", :published => false) get "/api/v1/articles/#{article.id}/children?#{params.to_query}" @@ -82,6 +91,74 @@ class ArticlesTest < ActiveSupport::TestCase assert_not_includes json['articles'].map {|a| a['id']}, child.id end + should 'perform a vote in a article identified by id' do + article = fast_create(Article, :profile_id => @person.id, :name => "Some thing") + @params[:value] = 1 + + post "/api/v1/articles/#{article.id}/vote?#{params.to_query}" + json = JSON.parse(last_response.body) + + assert_not_equal 401, last_response.status + assert_equal true, json['vote'] + end + + expose_attributes = %w(id body abstract created_at title author profile categories image votes_for votes_against setting position hits start_date end_date tag_list parent children children_count) + + expose_attributes.each do |attr| + should "expose article #{attr} attribute by default" do + article = fast_create(Article, :profile_id => user.person.id, :name => "Some thing") + get "/api/v1/articles/?#{params.to_query}" + json = JSON.parse(last_response.body) + assert json["articles"].last.has_key?(attr) + end + end + + should "update body of article created by me" do + new_value = "Another body" + params[:article] = {:body => new_value} + article = fast_create(Article, :profile_id => person.id) + post "/api/v1/articles/#{article.id}?#{params.to_query}" + json = JSON.parse(last_response.body) + assert_equal new_value, json["article"]["body"] + end + + should "update title of article created by me" do + new_value = "Another name" + params[:article] = {:name => new_value} + article = fast_create(Article, :profile_id => person.id) + post "/api/v1/articles/#{article.id}?#{params.to_query}" + json = JSON.parse(last_response.body) + assert_equal new_value, json["article"]["title"] + end + + should 'not update article of another user' do + another_person = fast_create(Person, :environment_id => environment.id) + article = fast_create(Article, :profile_id => another_person.id) + params[:article] = {:title => 'Some title'} + post "/api/v1/articles/#{article.id}?#{params.to_query}" + assert_equal 403, last_response.status + end + + should 'not update article without permission in community' do + community = fast_create(Community, :environment_id => environment.id) + article = fast_create(Article, :profile_id => community.id) + params[:article] = {:name => 'New title'} + post "/api/v1/articles/#{article.id}?#{params.to_query}" + assert_equal 403, last_response.status + end + + + should 'update article of community if user has permission' do + community = fast_create(Community, :environment_id => environment.id) + give_permission(person, 'post_content', community) + article = fast_create(Article, :profile_id => community.id) + new_value = "Another body" + params[:article] = {:body => new_value} + post "/api/v1/articles/#{article.id}?#{params.to_query}" + json = JSON.parse(last_response.body) + assert_equal new_value, json["article"]["body"] + end + should 'list articles with pagination' do Article.destroy_all article_one = fast_create(Article, :profile_id => user.person.id, :name => "Another thing", :created_at => 2.days.ago) @@ -126,7 +203,7 @@ class ArticlesTest < ActiveSupport::TestCase profile_kinds = %w(community person enterprise) profile_kinds.each do |kind| should "return article by #{kind}" do - profile = fast_create(kind.camelcase.constantize) + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id) article = fast_create(Article, :profile_id => profile.id, :name => "Some thing") get "/api/v1/#{kind.pluralize}/#{profile.id}/articles/#{article.id}?#{params.to_query}" json = JSON.parse(last_response.body) @@ -134,7 +211,7 @@ class ArticlesTest < ActiveSupport::TestCase end should "not return article by #{kind} if user has no permission to view it" do - profile = fast_create(kind.camelcase.constantize) + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id) article = fast_create(Article, :profile_id => profile.id, :name => "Some thing", :published => false) assert !article.published? @@ -143,7 +220,7 @@ class ArticlesTest < ActiveSupport::TestCase end should "not list forbidden article when listing articles by #{kind}" do - profile = fast_create(kind.camelcase.constantize) + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id) article = fast_create(Article, :profile_id => profile.id, :name => "Some thing", :published => false) assert !article.published? @@ -151,6 +228,29 @@ class ArticlesTest < ActiveSupport::TestCase json = JSON.parse(last_response.body) assert_not_includes json['articles'].map {|a| a['id']}, article.id end + + should "return article by #{kind} and path" do + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id) + parent_article = Folder.create!(:profile => profile, :name => "Parent Folder") + article = Article.create!(:profile => profile, :name => "Some thing", :parent => parent_article) + + params[:path] = parent_article.slug+'/'+article.slug + get "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}" + json = JSON.parse(last_response.body) + assert_equal article.id, json["article"]["id"] + end + + should "not return article by #{kind} and path if user has no permission to view it" do + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id) + parent_article = Folder.create!(:profile => profile, :name => "Parent Folder") + article = Article.create!(:profile => profile, :name => "Some thing", :parent => parent_article, :published => false) + + assert !article.published? + + params[:path] = parent_article.slug+'/'+article.slug + get "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}" + assert_equal 403, last_response.status + end end ############################# @@ -160,7 +260,7 @@ class ArticlesTest < ActiveSupport::TestCase group_kinds = %w(community enterprise) group_kinds.each do |kind| should "#{kind}: create article" do - profile = fast_create(kind.camelcase.constantize) + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id) give_permission(user.person, 'post_content', profile) params[:article] = {:name => "Title"} post "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}" @@ -169,16 +269,16 @@ class ArticlesTest < ActiveSupport::TestCase end should "#{kind}: do not create article if user has no permission to post content" do - profile = fast_create(kind.camelcase.constantize) + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id) give_permission(user.person, 'invite_members', profile) params[:article] = {:name => "Title"} post "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}" assert_equal 403, last_response.status end - should "#{kind}: create article with parent" do - profile = fast_create(kind.camelcase.constantize) - profile.add_member(user.person) + should "#{kind} create article with parent" do + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id) + Person.any_instance.stubs(:can_post_content?).with(profile).returns(true) article = fast_create(Article) params[:article] = {:name => "Title", :parent_id => article.id} @@ -187,9 +287,9 @@ class ArticlesTest < ActiveSupport::TestCase assert_equal article.id, json["article"]["parent"]["id"] end - should "#{kind}: create article with content type passed as parameter" do - profile = fast_create(kind.camelcase.constantize) - profile.add_member(user.person) + should "#{kind} create article with content type passed as parameter" do + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id) + Person.any_instance.stubs(:can_post_content?).with(profile).returns(true) Article.delete_all params[:article] = {:name => "Title"} @@ -201,8 +301,8 @@ class ArticlesTest < ActiveSupport::TestCase end should "#{kind}: create article of TinyMceArticle type if no content type is passed as parameter" do - profile = fast_create(kind.camelcase.constantize) - profile.add_member(user.person) + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id) + Person.any_instance.stubs(:can_post_content?).with(profile).returns(true) params[:article] = {:name => "Title"} post "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}" @@ -212,7 +312,7 @@ class ArticlesTest < ActiveSupport::TestCase end should "#{kind}: not create article with invalid article content type" do - profile = fast_create(kind.camelcase.constantize) + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id) profile.add_member(user.person) params[:article] = {:name => "Title"} @@ -223,20 +323,20 @@ class ArticlesTest < ActiveSupport::TestCase assert_equal 403, last_response.status end - should "#{kind}: create article defining the correct profile" do - profile = fast_create(kind.camelcase.constantize) - profile.add_member(user.person) + should "#{kind} create article defining the correct profile" do + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id) + Person.any_instance.stubs(:can_post_content?).with(profile).returns(true) params[:article] = {:name => "Title"} post "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}" json = JSON.parse(last_response.body) - assert_equal profile, Article.last.profile + assert_equal profile.id, json['article']['profile']['id'] end should "#{kind}: create article defining the created_by" do - profile = fast_create(kind.camelcase.constantize) - profile.add_member(user.person) + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id) + Person.any_instance.stubs(:can_post_content?).with(profile).returns(true) params[:article] = {:name => "Title"} post "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}" @@ -246,8 +346,8 @@ class ArticlesTest < ActiveSupport::TestCase end should "#{kind}: create article defining the last_changed_by" do - profile = fast_create(kind.camelcase.constantize) - profile.add_member(user.person) + profile = fast_create(kind.camelcase.constantize, :environment_id => environment.id) + Person.any_instance.stubs(:can_post_content?).with(profile).returns(true) params[:article] = {:name => "Title"} post "/api/v1/#{kind.pluralize}/#{profile.id}/articles?#{params.to_query}" @@ -269,7 +369,7 @@ class ArticlesTest < ActiveSupport::TestCase end should 'person do not create article if user has no permission to post content' do - person = fast_create(Person) + person = fast_create(Person, :environment_id => environment.id) params[:article] = {:name => "Title"} post "/api/v1/people/#{person.id}/articles?#{params.to_query}" assert_equal 403, last_response.status @@ -376,4 +476,99 @@ class ArticlesTest < ActiveSupport::TestCase assert_equal [0, 1, 1], [a1.reload.hits, a2.reload.hits, a3.reload.hits] end + should 'update hit attribute of article specific children' do + a1 = fast_create(Article, :profile_id => user.person.id) + a2 = fast_create(Article, :parent_id => a1.id, :profile_id => user.person.id) + get "/api/v1/articles/#{a1.id}/children/#{a2.id}?#{params.to_query}" + json = JSON.parse(last_response.body) + assert_equal 1, json['article']['hits'] + end + + should 'list all events of a community in a given category' do + co = Community.create(identifier: 'my-community', name: 'name-my-community') + c1 = Category.create(environment: Environment.default, name: 'my-category') + c2 = Category.create(environment: Environment.default, name: 'dont-show-me-this-category') + e1 = fast_create(Event, :profile_id => co.id) + e2 = fast_create(Event, :profile_id => co.id) + e1.categories << c1 + e2.categories << c2 + e1.save! + e2.save! + params['content_type']='Event' + get "api/v1/communities/#{co.id}/articles?#{params.to_query}" + json = JSON.parse(last_response.body) + assert_equal json['articles'].count, 2 + end + + should 'list a event of a community in a given category' do + co = Community.create(identifier: 'my-community', name: 'name-my-community') + c1 = Category.create(environment: Environment.default, name: 'my-category') + c2 = Category.create(environment: Environment.default, name: 'dont-show-me-this-category') + e1 = fast_create(Event, :profile_id => co.id) + e2 = fast_create(Event, :profile_id => co.id) + e1.categories << c1 + e2.categories << c2 + e1.save! + e2.save! + params['category_ids[]']=c1.id + params['content_type']='Event' + get "api/v1/communities/#{co.id}/articles?#{params.to_query}" + json = JSON.parse(last_response.body) + #should show only one article, since the other not in the same category + assert_equal 1, json['articles'].count + assert_equal e1.id, json['articles'][0]['id'] + end + + should 'not list uncategorized event of a community if a category is given' do + co = Community.create(identifier: 'my-community', name: 'name-my-community') + c1 = Category.create(environment: Environment.default, name: 'my-category') + c2 = Category.create(environment: Environment.default, name: 'dont-show-me-this-category') + e1 = fast_create(Event, :profile_id => co.id) + e2 = fast_create(Event, :profile_id => co.id) + e3 = fast_create(Event, :profile_id => co.id) + e1.categories << c1 + e2.categories << c2 + params['category_ids[]']=c1.id + params['content_type']='Event' + get "api/v1/communities/#{co.id}/articles?#{params.to_query}" + json = JSON.parse(last_response.body) + assert_equal 1, json['articles'].count + assert_equal e1.id, json['articles'][0]['id'] + end + + should 'list events of a community in a given 2 categories' do + co = Community.create(identifier: 'my-community', name: 'name-my-community') + c1 = Category.create(environment: Environment.default, name: 'my-category') + c2 = Category.create(environment: Environment.default, name: 'dont-show-me-this-category') + e1 = fast_create(Event, :profile_id => co.id) + e2 = fast_create(Event, :profile_id => co.id) + e1.categories << c1 + e2.categories << c2 + e1.save! + e2.save! + params['content_type']='Event' + params['categories_ids'] = [c1.id, c2.id] + get "api/v1/communities/#{co.id}/articles?#{params.to_query}" + json = JSON.parse(last_response.body) + assert_equal json['articles'].count, 2 + end + + should 'Show 2 events since it uses an IN operator for category instead of an OR' do + co = Community.create(identifier: 'my-community', name: 'name-my-community') + c1 = Category.create(environment: Environment.default, name: 'my-category') + c2 = Category.create(environment: Environment.default, name: 'dont-show-me-this-category') + c3 = Category.create(environment: Environment.default, name: 'extra-category') + e1 = fast_create(Event, :profile_id => co.id) + e2 = fast_create(Event, :profile_id => co.id) + e1.categories << c1 + e2.categories << c2 + e1.save! + e2.save! + params['content_type']='Event' + params['categories_ids'] = [c1.id, c2.id, c3.id] + get "api/v1/communities/#{co.id}/articles?#{params.to_query}" + json = JSON.parse(last_response.body) + assert_equal json['articles'].count, 2 + end + end diff --git a/test/unit/api/categories_test.rb b/test/unit/api/categories_test.rb index 04466b3..f4db3d1 100644 --- a/test/unit/api/categories_test.rb +++ b/test/unit/api/categories_test.rb @@ -7,25 +7,25 @@ class CategoriesTest < ActiveSupport::TestCase end should 'list categories' do - category = fast_create(Category) + category = fast_create(Category, :environment_id => environment.id) get "/api/v1/categories/?#{params.to_query}" json = JSON.parse(last_response.body) assert_includes json["categories"].map { |c| c["name"] }, category.name end should 'get category by id' do - category = fast_create(Category) + category = fast_create(Category, :environment_id => environment.id) get "/api/v1/categories/#{category.id}/?#{params.to_query}" json = JSON.parse(last_response.body) assert_equal category.name, json["category"]["name"] end should 'list parent and children when get category by id' do - parent = fast_create(Category) - child_1 = fast_create(Category) - child_2 = fast_create(Category) + parent = fast_create(Category, :environment_id => environment.id) + child_1 = fast_create(Category, :environment_id => environment.id) + child_2 = fast_create(Category, :environment_id => environment.id) - category = fast_create(Category) + category = fast_create(Category, :environment_id => environment.id) category.parent = parent category.children << child_1 category.children << child_2 @@ -33,16 +33,16 @@ class CategoriesTest < ActiveSupport::TestCase get "/api/v1/categories/#{category.id}/?#{params.to_query}" json = JSON.parse(last_response.body) - assert_equal({'id' => parent.id, 'name' => parent.name}, json['category']['parent']) + assert_equal({'id' => parent.id, 'name' => parent.name, 'slug' => parent.slug}, json['category']['parent']) assert_equivalent [child_1.id, child_2.id], json['category']['children'].map { |c| c['id'] } end should 'include parent in categories list if params is true' do - parent_1 = fast_create(Category) # parent_1 has no parent category - child_1 = fast_create(Category) - child_2 = fast_create(Category) + parent_1 = fast_create(Category, :environment_id => environment.id) # parent_1 has no parent category + child_1 = fast_create(Category, :environment_id => environment.id) + child_2 = fast_create(Category, :environment_id => environment.id) - parent_2 = fast_create(Category) + parent_2 = fast_create(Category, :environment_id => environment.id) parent_2.parent = parent_1 parent_2.children << child_1 parent_2.children << child_2 @@ -60,10 +60,10 @@ class CategoriesTest < ActiveSupport::TestCase end should 'include children in categories list if params is true' do - category = fast_create(Category) - child_1 = fast_create(Category) - child_2 = fast_create(Category) - child_3 = fast_create(Category) + category = fast_create(Category, :environment_id => environment.id) + child_1 = fast_create(Category, :environment_id => environment.id) + child_2 = fast_create(Category, :environment_id => environment.id) + child_3 = fast_create(Category, :environment_id => environment.id) category.children << child_1 category.children << child_2 @@ -83,4 +83,15 @@ class CategoriesTest < ActiveSupport::TestCase json["categories"].map{ |c| c['children'].map{ |child| child['id'] }.sort } end + expose_attributes = %w(id name full_name image display_color) + + expose_attributes.each do |attr| + should "expose category #{attr} attribute by default" do + category = fast_create(Category, :environment_id => environment.id) + get "/api/v1/categories/?#{params.to_query}" + json = JSON.parse(last_response.body) + assert json["categories"].last.has_key?(attr) + end + end + end diff --git a/test/unit/api/comments_test.rb b/test/unit/api/comments_test.rb index 5115297..d68c4b6 100644 --- a/test/unit/api/comments_test.rb +++ b/test/unit/api/comments_test.rb @@ -65,4 +65,17 @@ class CommentsTest < ActiveSupport::TestCase assert_equal 201, last_response.status assert_equal body, json['comment']['body'] end + + should 'comment creation define the source' do + amount = Comment.count + article = fast_create(Article, :profile_id => user.person.id, :name => "Some thing") + body = 'My comment' + params.merge!({:body => body}) + + post "/api/v1/articles/#{article.id}/comments?#{params.to_query}" + assert_equal amount + 1, Comment.count + comment = Comment.last + assert_not_nil comment.source + end + end diff --git a/test/unit/api/communities_test.rb b/test/unit/api/communities_test.rb index 7ced638..df59c18 100644 --- a/test/unit/api/communities_test.rb +++ b/test/unit/api/communities_test.rb @@ -8,8 +8,8 @@ class CommunitiesTest < ActiveSupport::TestCase end should 'list only communities' do - community = fast_create(Community) - enterprise = fast_create(Enterprise) # should not list this enterprise + community = fast_create(Community, :environment_id => environment.id) + enterprise = fast_create(Enterprise, :environment_id => environment.id) # should not list this enterprise get "/api/v1/communities?#{params.to_query}" json = JSON.parse(last_response.body) assert_not_includes json['communities'].map {|c| c['id']}, enterprise.id @@ -17,16 +17,16 @@ class CommunitiesTest < ActiveSupport::TestCase end should 'list all communities' do - community1 = fast_create(Community, :public_profile => true) - community2 = fast_create(Community) + community1 = fast_create(Community, :environment_id => environment.id, :public_profile => true) + community2 = fast_create(Community, :environment_id => environment.id) get "/api/v1/communities?#{params.to_query}" json = JSON.parse(last_response.body) assert_equivalent [community1.id, community2.id], json['communities'].map {|c| c['id']} end should 'not list invisible communities' do - community1 = fast_create(Community) - fast_create(Community, :visible => false) + community1 = fast_create(Community, :environment_id => environment.id) + fast_create(Community, :environment_id => environment.id, :visible => false) get "/api/v1/communities?#{params.to_query}" json = JSON.parse(last_response.body) @@ -34,8 +34,8 @@ class CommunitiesTest < ActiveSupport::TestCase end should 'not list private communities without permission' do - community1 = fast_create(Community) - fast_create(Community, :public_profile => false) + community1 = fast_create(Community, :environment_id => environment.id) + fast_create(Community, :environment_id => environment.id, :public_profile => false) get "/api/v1/communities?#{params.to_query}" json = JSON.parse(last_response.body) @@ -43,8 +43,8 @@ class CommunitiesTest < ActiveSupport::TestCase end should 'list private community for members' do - c1 = fast_create(Community) - c2 = fast_create(Community, :public_profile => false) + c1 = fast_create(Community, :environment_id => environment.id) + c2 = fast_create(Community, :environment_id => environment.id, :public_profile => false) c2.add_member(person) get "/api/v1/communities?#{params.to_query}" @@ -66,7 +66,7 @@ class CommunitiesTest < ActiveSupport::TestCase end should 'get community' do - community = fast_create(Community) + community = fast_create(Community, :environment_id => environment.id) get "/api/v1/communities/#{community.id}?#{params.to_query}" json = JSON.parse(last_response.body) @@ -74,7 +74,7 @@ class CommunitiesTest < ActiveSupport::TestCase end should 'not get invisible community' do - community = fast_create(Community, :visible => false) + community = fast_create(Community, :environment_id => environment.id, :visible => false) get "/api/v1/communities/#{community.id}?#{params.to_query}" json = JSON.parse(last_response.body) @@ -82,8 +82,8 @@ class CommunitiesTest < ActiveSupport::TestCase end should 'not get private communities without permission' do - community = fast_create(Community) - fast_create(Community, :public_profile => false) + community = fast_create(Community, :environment_id => environment.id) + fast_create(Community, :environment_id => environment.id, :public_profile => false) get "/api/v1/communities/#{community.id}?#{params.to_query}" json = JSON.parse(last_response.body) @@ -91,17 +91,18 @@ class CommunitiesTest < ActiveSupport::TestCase end should 'get private community for members' do - community = fast_create(Community, :public_profile => false) + community = fast_create(Community, :environment_id => environment.id, :public_profile => false, :visible => true) community.add_member(person) + get "/api/v1/communities/#{community.id}?#{params.to_query}" json = JSON.parse(last_response.body) assert_equal community.id, json['community']['id'] end should 'list person communities' do - community = fast_create(Community) - fast_create(Community) + community = fast_create(Community, :environment_id => environment.id) + fast_create(Community, :environment_id => environment.id) community.add_member(person) get "/api/v1/people/#{person.id}/communities?#{params.to_query}" @@ -110,8 +111,8 @@ class CommunitiesTest < ActiveSupport::TestCase end should 'not list person communities invisible' do - c1 = fast_create(Community) - c2 = fast_create(Community, :visible => false) + c1 = fast_create(Community, :environment_id => environment.id) + c2 = fast_create(Community, :environment_id => environment.id, :visible => false) c1.add_member(person) c2.add_member(person) diff --git a/test/unit/api/enterprises_test.rb b/test/unit/api/enterprises_test.rb index a3e25d4..1ec04ee 100644 --- a/test/unit/api/enterprises_test.rb +++ b/test/unit/api/enterprises_test.rb @@ -8,8 +8,8 @@ class EnterprisesTest < ActiveSupport::TestCase end should 'list only enterprises' do - community = fast_create(Community) # should not list this community - enterprise = fast_create(Enterprise, :public_profile => true) + community = fast_create(Community, :environment_id => environment.id) # should not list this community + enterprise = fast_create(Enterprise, :environment_id => environment.id, :public_profile => true) get "/api/v1/enterprises?#{params.to_query}" json = JSON.parse(last_response.body) assert_includes json['enterprises'].map {|c| c['id']}, enterprise.id @@ -17,15 +17,15 @@ class EnterprisesTest < ActiveSupport::TestCase end should 'list all enterprises' do - enterprise1 = fast_create(Enterprise, :public_profile => true) - enterprise2 = fast_create(Enterprise) + enterprise1 = fast_create(Enterprise, :environment_id => environment.id, :public_profile => true) + enterprise2 = fast_create(Enterprise, :environment_id => environment.id) get "/api/v1/enterprises?#{params.to_query}" json = JSON.parse(last_response.body) assert_equivalent [enterprise1.id, enterprise2.id], json['enterprises'].map {|c| c['id']} end should 'not list invisible enterprises' do - enterprise1 = fast_create(Enterprise) + enterprise1 = fast_create(Enterprise, :environment_id => environment.id) fast_create(Enterprise, :visible => false) get "/api/v1/enterprises?#{params.to_query}" @@ -34,8 +34,8 @@ class EnterprisesTest < ActiveSupport::TestCase end should 'not list private enterprises without permission' do - enterprise1 = fast_create(Enterprise) - fast_create(Enterprise, :public_profile => false) + enterprise1 = fast_create(Enterprise, :environment_id => environment.id) + fast_create(Enterprise, :environment_id => environment.id, :public_profile => false) get "/api/v1/enterprises?#{params.to_query}" json = JSON.parse(last_response.body) @@ -43,8 +43,8 @@ class EnterprisesTest < ActiveSupport::TestCase end should 'list private enterprise for members' do - c1 = fast_create(Enterprise) - c2 = fast_create(Enterprise, :public_profile => false) + c1 = fast_create(Enterprise, :environment_id => environment.id) + c2 = fast_create(Enterprise, :environment_id => environment.id, :public_profile => false) c2.add_member(person) get "/api/v1/enterprises?#{params.to_query}" @@ -53,7 +53,7 @@ class EnterprisesTest < ActiveSupport::TestCase end should 'get enterprise' do - enterprise = fast_create(Enterprise) + enterprise = fast_create(Enterprise, :environment_id => environment.id) get "/api/v1/enterprises/#{enterprise.id}?#{params.to_query}" json = JSON.parse(last_response.body) @@ -69,8 +69,8 @@ class EnterprisesTest < ActiveSupport::TestCase end should 'not get private enterprises without permission' do - enterprise = fast_create(Enterprise) - fast_create(Enterprise, :public_profile => false) + enterprise = fast_create(Enterprise, :environment_id => environment.id) + fast_create(Enterprise, :environment_id => environment.id, :public_profile => false) get "/api/v1/enterprises/#{enterprise.id}?#{params.to_query}" json = JSON.parse(last_response.body) @@ -87,8 +87,8 @@ class EnterprisesTest < ActiveSupport::TestCase end should 'list person enterprises' do - enterprise = fast_create(Enterprise) - fast_create(Enterprise) + enterprise = fast_create(Enterprise, :environment_id => environment.id) + fast_create(Enterprise, :environment_id => environment.id) enterprise.add_member(person) get "/api/v1/people/#{person.id}/enterprises?#{params.to_query}" @@ -97,8 +97,8 @@ class EnterprisesTest < ActiveSupport::TestCase end should 'not list person enterprises invisible' do - c1 = fast_create(Enterprise) - c2 = fast_create(Enterprise, :visible => false) + c1 = fast_create(Enterprise, :environment_id => environment.id) + c2 = fast_create(Enterprise, :environment_id => environment.id, :visible => false) c1.add_member(person) c2.add_member(person) diff --git a/test/unit/api/helpers_test.rb b/test/unit/api/helpers_test.rb index 6c7d19e..f1f9c33 100644 --- a/test/unit/api/helpers_test.rb +++ b/test/unit/api/helpers_test.rb @@ -25,15 +25,6 @@ class APIHelpersTest < ActiveSupport::TestCase assert_equal user, current_user end - should 'not get the current user with expired token' do - user = create_user('someuser') - user.generate_private_token! - user.private_token_generated_at = DateTime.now.prev_year - user.save - self.params = {:private_token => user.private_token} - assert_nil current_user - end - should 'get the person of current user' do user = create_user('someuser') user.generate_private_token! @@ -161,6 +152,39 @@ class APIHelpersTest < ActiveSupport::TestCase assert_nil make_conditions_with_parameter[:type] end + #test_should_make_order_with_parameters_return_order_if attribute_is_found_at_object_association + should 'make_order_with_parameters return order if attribute is found at object association' do + environment = Environment.new + params = {:order => "name ASC"} + assert_equal "name ASC", make_order_with_parameters(environment, "articles", params) + end + + # test added to check for eventual sql injection vunerabillity + #test_should_make_order_with_parameters_return_default_order_if_attributes_not_exists + should 'make_order_with_parameters return default order if attributes not exists' do + environment = Environment.new + params = {:order => "CRAZY_FIELD ASC"} # quote used to check sql injection vunerabillity + assert_equal "created_at DESC", make_order_with_parameters(environment, "articles", params) + end + + should 'make_order_with_parameters return default order if sql injection detected' do + environment = Environment.new + params = {:order => "name' ASC"} # quote used to check sql injection vunerabillity + assert_equal "created_at DESC", make_order_with_parameters(environment, "articles", params) + end + + should 'make_order_with_parameters return RANDOM() if random is passed' do + environment = Environment.new + params = {:order => "random"} # quote used to check sql injection vunerabillity + assert_equal "RANDOM()", make_order_with_parameters(environment, "articles", params) + end + + should 'make_order_with_parameters return RANDOM() if random function is passed' do + environment = Environment.new + params = {:order => "random()"} # quote used to check sql injection vunerabillity + assert_equal "RANDOM()", make_order_with_parameters(environment, "articles", params) + end + should 'render not_found if endpoint is unavailable' do Noosfero::API::API.stubs(:endpoint_unavailable?).returns(true) self.expects(:not_found!) diff --git a/test/unit/api/people_test.rb b/test/unit/api/people_test.rb index 5f20472..07dd9da 100644 --- a/test/unit/api/people_test.rb +++ b/test/unit/api/people_test.rb @@ -148,4 +148,21 @@ class PeopleTest < ActiveSupport::TestCase get "/api/v1/people/#{some_person.id}/permissions?#{params.to_query}" assert_equal 403, last_response.status end + + should 'not update another person' do + person = fast_create(Person, :environment_id => environment.id) + post "/api/v1/people/#{person.id}?#{params.to_query}" + assert_equal 403, last_response.status + end + + should 'update yourself' do + another_name = 'Another Name' + params[:person] = {} + params[:person][:name] = another_name + assert_not_equal another_name, person.name + post "/api/v1/people/#{person.id}?#{params.to_query}" + person.reload + assert_equal another_name, person.name + end + end diff --git a/test/unit/api/search_test.rb b/test/unit/api/search_test.rb new file mode 100644 index 0000000..cb080e5 --- /dev/null +++ b/test/unit/api/search_test.rb @@ -0,0 +1,138 @@ +require File.dirname(__FILE__) + '/test_helper' + +class SearchTest < ActiveSupport::TestCase + + def setup + @person = create_user('testing').person + end + attr_reader :person + + should 'not list unpublished articles' do + Article.delete_all + article = fast_create(Article, :profile_id => person.id, :published => false) + assert !article.published? + get "/api/v1/search/article" + json = JSON.parse(last_response.body) + assert_empty json['articles'] + end + + should 'list articles' do + fast_create(Article, :profile_id => person.id) + get "/api/v1/search/article" + json = JSON.parse(last_response.body) + assert_not_empty json['articles'] + end + + should 'invalid search string articles' do + fast_create(Article, :profile_id => person.id, :name => 'some article') + get "/api/v1/search/article?query=test" + json = JSON.parse(last_response.body) + assert_empty json['articles'] + end + + should 'do not list articles of wrong type' do + fast_create(Article, :profile_id => person.id) + get "/api/v1/search/article?type=TinyMceArticle" + json = JSON.parse(last_response.body) + assert_empty json['articles'] + end + + should 'list articles of one type' do + fast_create(Article, :profile_id => person.id) + article = fast_create(TinyMceArticle, :profile_id => person.id) + + get "/api/v1/search/article?type=TinyMceArticle" + json = JSON.parse(last_response.body) + assert_equal article.id, json['articles'].first['id'] + end + + should 'list articles of one type and query string' do + fast_create(Article, :profile_id => person.id, :name => 'some article') + fast_create(Article, :profile_id => person.id, :name => 'Some thing') + article = fast_create(TinyMceArticle, :profile_id => person.id, :name => 'Some thing') + get "/api/v1/search/article?type=TinyMceArticle&query=thing" + json = JSON.parse(last_response.body) + assert_equal 1, json['articles'].count + assert_equal article.id, json['articles'].first['id'] + end + + should 'not return more entries than page limit' do + 1.upto(5).each do |n| + fast_create(Article, :profile_id => person.id, :name => "Article #{n}") + end + + get "/api/v1/search/article?query=Article&per_page=3" + json = JSON.parse(last_response.body) + + assert_equal 3, json['articles'].count + end + + should 'return entries second page' do + 1.upto(5).each do |n| + fast_create(Article, :profile_id => person.id, :name => "Article #{n}") + end + + get "/api/v1/search/article?query=Article&per_page=3&page=2" + json = JSON.parse(last_response.body) + + assert_equal 2, json['articles'].count + end + + should 'search articles in profile' do + person2 = fast_create(Person) + fast_create(Article, :profile_id => person.id) + fast_create(Article, :profile_id => person.id) + article = fast_create(Article, :profile_id => person2.id) + + get "/api/v1/search/article?query=Article&profile_id=#{person2.id}" + json = JSON.parse(last_response.body) + assert_equal article.id, json['articles'].first['id'] + end + + should 'search and return values specified in fields parameter' do + fast_create(Article, :profile_id => person.id) + get "/api/v1/search/article?fields=title" + json = JSON.parse(last_response.body) + assert_not_empty json['articles'] + assert_equal ['title'], json['articles'].first.keys + end + + should 'search with parent' do + parent = fast_create(Folder, :profile_id => person.id) + fast_create(Article, :profile_id => person.id) + article = fast_create(Article, :profile_id => person.id, :parent_id => parent.id) + get "/api/v1/search/article?parent_id=#{parent.id}" + json = JSON.parse(last_response.body) + assert_equal 1, json['articles'].count + assert_equal article.id, json['articles'].first["id"] + end + + should 'search filter by category' do + Article.delete_all + fast_create(Article, :profile_id => person.id) + article = fast_create(Article, :profile_id => person.id) + category = fast_create(Category) + article.categories<< category + get "/api/v1/search/article?category_ids=#{category.id}" + json = JSON.parse(last_response.body) + assert_equal 1, json['articles'].count + assert_equal article.id, json['articles'].first["id"] + end + + should 'search filter by more than one category' do + Article.delete_all + fast_create(Article, :profile_id => person.id) + article1 = fast_create(Article, :profile_id => person.id) + article2 = fast_create(Article, :profile_id => person.id) + category1 = fast_create(Category) + category2 = fast_create(Category) + article1.categories<< category1 + article2.categories<< category2 + get "/api/v1/search/article?category_ids[]=#{category1.id}&category_ids[]=#{category2.id}" + json = JSON.parse(last_response.body) + assert_equal 2, json['articles'].count + assert_equal article1.id, json['articles'].first["id"] + assert_equal article2.id, json['articles'].last["id"] + end + +end diff --git a/test/unit/api/session_test.rb b/test/unit/api/session_test.rb index ff1e7a2..742e21e 100644 --- a/test/unit/api/session_test.rb +++ b/test/unit/api/session_test.rb @@ -10,7 +10,7 @@ class SessionTest < ActiveSupport::TestCase params = {:login => "testapi", :password => "testapi"} post "/api/v1/login?#{params.to_query}" json = JSON.parse(last_response.body) - assert !json["private_token"].blank? + assert !json['user']["private_token"].blank? end should 'return 401 when login fails' do @@ -21,22 +21,167 @@ class SessionTest < ActiveSupport::TestCase end should 'register a user' do - params = {:login => "newuserapi", :password => "newuserapi", :email => "newuserapi@email.com" } + Environment.default.enable('skip_new_user_email_confirmation') + params = {:login => "newuserapi", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com" } post "/api/v1/register?#{params.to_query}" assert_equal 201, last_response.status + json = JSON.parse(last_response.body) + assert User['newuserapi'].activated? + assert json['user']['activated'] + assert json['user']['private_token'].present? + end + + should 'register a user with name' do + Environment.default.enable('skip_new_user_email_confirmation') + params = {:login => "newuserapi", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com", :name => "Little John" } + post "/api/v1/register?#{params.to_query}" + assert_equal 201, last_response.status + json = JSON.parse(last_response.body) + assert json['user']['activated'] + assert json['user']['private_token'].present? + end + + should 'register an inactive user' do + params = {:login => "newuserapi", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com" } + post "/api/v1/register?#{params.to_query}" + assert_equal 201, last_response.status + json = JSON.parse(last_response.body) + assert !json['activated'] + assert json['private_token'].blank? end - should 'do not register a user without email' do - params = {:login => "newuserapi", :password => "newuserapi", :email => nil } + should 'not register a user with invalid login' do + params = {:login => "c", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com" } post "/api/v1/register?#{params.to_query}" assert_equal 400, last_response.status + json = JSON.parse(last_response.body) + msg = json['message'].split(':') + key = msg[0][2, 5] + val = msg[1][2, 38] + assert_equal "login", key + assert_equal "is too short (minimum is 2 characters)", val end - should 'do not register a duplicated user' do - params = {:login => "newuserapi", :password => "newuserapi", :email => "newuserapi@email.com" } + should 'not register a user with invalid login pt' do + I18n.locale = "pt-BR" + params = {:lang => "pt-BR", :login => "c", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com" } post "/api/v1/register?#{params.to_query}" + assert_equal 400, last_response.status + json = JSON.parse(last_response.body) + msg = json['message'].split(':') + key = msg[0][2, 5] + val = msg[1][2, 35] + assert_equal "login", key + assert val.include? "muito curto" + end + + should 'not register a user without email' do + params = {:login => "newuserapi", :password => "newuserapi", :password_confirmation => "newuserapi", :email => nil } post "/api/v1/register?#{params.to_query}" assert_equal 400, last_response.status end + should 'not register a duplicated user' do + params = {:login => "newuserapi", :password => "newuserapi", :password_confirmation => "newuserapi", :email => "newuserapi@email.com" } + post "/api/v1/register?#{params.to_query}" + post "/api/v1/register?#{params.to_query}" + assert_equal 400, last_response.status + json = JSON.parse(last_response.body) + end + + # TODO: Add another test cases to check register situations + should 'activate a user' do + params = { + :login => "newuserapi", + :password => "newuserapi", + :password_confirmation => "newuserapi", + :email => "newuserapi@email.com" + } + user = User.new(params) + user.save! + + params = { activation_code: user.activation_code} + patch "/api/v1/activate?#{params.to_query}" + assert_equal 200, last_response.status + end + + should 'do not activate a user if admin must approve him' do + params = { + :login => "newuserapi", + :password => "newuserapi", + :password_confirmation => "newuserapi", + :email => "newuserapi@email.com", + :environment => Environment.default + } + user = User.new(params) + user.environment.enable('admin_must_approve_new_users') + user.save! + + params = { activation_code: user.activation_code} + patch "/api/v1/activate?#{params.to_query}" + assert_equal 202, last_response.status + assert_equal 'Waiting for admin moderate user registration', JSON.parse(last_response.body)["message"] + end + + should 'do not activate a user if the token is invalid' do + params = { + :login => "newuserapi", + :password => "newuserapi", + :password_confirmation => "newuserapi", + :email => "newuserapi@email.com", + :environment => Environment.default + } + user = User.new(params) + user.save! + + params = { activation_code: '70250abe20cc6a67ef9399cf3286cb998b96aeaf'} + patch "/api/v1/activate?#{params.to_query}" + assert_equal 412, last_response.status + end + + should 'create task to change password by user login' do + user = create_user + params = {:value => user.login} + assert_difference 'ChangePassword.count' do + post "/api/v1/forgot_password?#{params.to_query}" + end + end + + should 'not create task to change password when user is not found' do + params = {:value => 'wronglogin'} + assert_no_difference 'ChangePassword.count' do + post "/api/v1/forgot_password?#{params.to_query}" + end + assert_equal 404, last_response.status + end + + should 'change user password and close task' do + user = create_user + user.activate + task = ChangePassword.create!(:requestor => user.person) + params = {:code => task.code, :password => 'secret', :password_confirmation => 'secret'} + patch "/api/v1/new_password?#{params.to_query}" + assert_equal Task::Status::FINISHED, task.reload.status + assert user.reload.authenticated?('secret') + json = JSON.parse(last_response.body) + assert_equal user.id, json['user']['id'] + end + + should 'do not change user password when password confirmation is wrong' do + user = create_user + user.activate + task = ChangePassword.create!(:requestor => user.person) + params = {:code => task.code, :password => 'secret', :password_confirmation => 's3cret'} + patch "/api/v1/new_password?#{params.to_query}" + assert_equal Task::Status::ACTIVE, task.reload.status + assert !user.reload.authenticated?('secret') + assert_equal 400, last_response.status + end + + should 'render not found when provide a wrong code on password change' do + params = {:code => "wrongcode", :password => 'secret', :password_confirmation => 'secret'} + patch "/api/v1/new_password?#{params.to_query}" + assert_equal 404, last_response.status + end + end diff --git a/test/unit/api/test_helper.rb b/test/unit/api/test_helper.rb index 7cc9f3e..fb6cbb8 100644 --- a/test/unit/api/test_helper.rb +++ b/test/unit/api/test_helper.rb @@ -9,16 +9,22 @@ class ActiveSupport::TestCase end def login_api - @user = User.create!(:login => 'testapi', :password => 'testapi', :password_confirmation => 'testapi', :email => 'test@test.org', :environment => Environment.default) + @environment = Environment.default + @user = User.create!(:login => 'testapi', :password => 'testapi', :password_confirmation => 'testapi', :email => 'test@test.org', :environment => @environment) @user.activate @person = @user.person post "/api/v1/login?login=testapi&password=testapi" json = JSON.parse(last_response.body) @private_token = json["private_token"] + unless @private_token + @user.generate_private_token! + @private_token = @user.private_token + end + @params = {:private_token => @private_token} end - attr_accessor :private_token, :user, :person, :params + attr_accessor :private_token, :user, :person, :params, :environment private diff --git a/test/unit/api/users_test.rb b/test/unit/api/users_test.rb new file mode 100644 index 0000000..e0c5980 --- /dev/null +++ b/test/unit/api/users_test.rb @@ -0,0 +1,62 @@ +# encoding: UTF-8 +require File.dirname(__FILE__) + '/test_helper' + +class UsersTest < ActiveSupport::TestCase + + def setup + login_api + end + + should 'list users' do + get "/api/v1/users/?#{params.to_query}" + json = JSON.parse(last_response.body) + assert_includes json["users"].map { |a| a["login"] }, user.login + end + + should 'create a user' do + params[:user] = {:login => 'some', :password => '123456', :password_confirmation => '123456', :email => 'some@some.com'} + post "/api/v1/users?#{params.to_query}" + json = JSON.parse(last_response.body) + assert_equal 'some', json['user']['login'] + end + + should 'not create duplicate user' do + params[:lang] = :"pt-BR" + params[:user] = {:login => 'some', :password => '123456', :password_confirmation => '123456', :email => 'some@some.com'} + post "/api/v1/users?#{params.to_query}" + json = JSON.parse(last_response.body) + assert_equal 'some', json['user']['login'] + params[:user] = {:login => 'some', :password => '123456', :password_confirmation => '123456', :email => 'some@some.com'} + post "/api/v1/users?#{params.to_query}" + json = JSON.parse(last_response.body) + assert_equal 'Username / Email já está em uso,e-Mail já está em uso', json['message'] + end + + should 'return 400 status for invalid user creation' do + params[:user] = {:login => 'some'} + post "/api/v1/users?#{params.to_query}" + json = JSON.parse(last_response.body) + assert_equal 400, last_response.status + end + + should 'get user' do + get "/api/v1/users/#{user.id}?#{params.to_query}" + json = JSON.parse(last_response.body) + assert_equal user.id, json['user']['id'] + end + + should 'list user permissions' do + community = fast_create(Community) + community.add_admin(person) + get "/api/v1/users/#{user.id}/?#{params.to_query}" + json = JSON.parse(last_response.body) + assert_includes json["user"]["permissions"], community.identifier + end + + should 'get logged user' do + get "/api/v1/users/me?#{params.to_query}" + json = JSON.parse(last_response.body) + assert_equal user.id, json['user']['id'] + end + +end -- libgit2 0.21.2