Merge pull request #22 from frankrousseau/api-openid-updates

WIP: API OpenID Updates
This commit is contained in:
Frank Rousseau 2018-12-27 00:24:02 +01:00 committed by GitHub
commit 8ffb814166
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 1461 additions and 358 deletions

View file

@ -3,12 +3,12 @@
module Api
module V1
class AspectsController < Api::V1::BaseController
before_action only: %i[index show] do
require_access_token %w[read]
before_action except: %i[create update destroy] do
require_access_token %w[contacts:read]
end
before_action only: %i[create update destroy] do
require_access_token %w[read write]
require_access_token %w[contacts:modify]
end
def index

View file

@ -52,6 +52,14 @@ module Api
def time_pager(query)
Api::Paging::RestPaginatorBuilder.new(query, request).time_pager(params)
end
def private_read?
access_token? %w[private:read]
end
def private_modify?
access_token? %w[private:modify]
end
end
end
end

View file

@ -3,12 +3,12 @@
module Api
module V1
class CommentsController < Api::V1::BaseController
before_action only: %i[index report] do
require_access_token %w[read]
before_action do
require_access_token %w[interactions public:read]
end
before_action only: %i[create destroy] do
require_access_token %w[write]
require_access_token %w[interactions public:modify]
end
rescue_from ActiveRecord::RecordNotFound do
@ -29,6 +29,7 @@ module Api
end
def index
find_post
comments_query = comment_service.find_for_post(params[:post_id])
params[:after] = Time.utc(1900).iso8601 if params.permit(:before, :after).empty?
comments_page = time_pager(comments_query).response
@ -37,6 +38,7 @@ module Api
end
def destroy
find_post
if comment_and_post_validate(params[:post_id], params[:id])
comment_service.destroy!(params[:id])
head :no_content
@ -46,6 +48,7 @@ module Api
end
def report
find_post
post_guid = params.require(:post_id)
comment_guid = params.require(:comment_id)
return unless comment_and_post_validate(post_guid, comment_guid)
@ -94,9 +97,19 @@ module Api
@comment_service ||= CommentService.new(current_user)
end
def post_service
@post_service ||= PostService.new(current_user)
end
def comment_as_json(comment)
CommentPresenter.new(comment).as_api_response
end
def find_post
post = post_service.find!(params[:post_id])
return post if post.public? || private_read?
raise ActiveRecord::RecordNotFound
end
end
end
end

View file

@ -5,12 +5,12 @@ require "api/paging/index_paginator"
module Api
module V1
class ContactsController < Api::V1::BaseController
before_action only: %i[index] do
require_access_token %w[read]
before_action except: %i[create destroy] do
require_access_token %w[contacts:read]
end
before_action only: %i[create destroy] do
require_access_token %w[read write]
require_access_token %w[contacts:modify]
end
rescue_from ActiveRecord::RecordNotFound do

View file

@ -5,12 +5,8 @@ module Api
class ConversationsController < Api::V1::BaseController
include ConversationsHelper
before_action only: %i[create index show] do
require_access_token %w[read]
end
before_action only: %i[create destroy] do
require_access_token %w[read write]
before_action do
require_access_token %w[conversations]
end
rescue_from ActiveRecord::RecordNotFound do

View file

@ -3,12 +3,8 @@
module Api
module V1
class LikesController < Api::V1::BaseController
before_action only: %i[show] do
require_access_token %w[read]
end
before_action only: %i[create destroy] do
require_access_token %w[write]
before_action do
require_access_token %w[interactions public:read]
end
rescue_from ActiveRecord::RecordNotFound do
@ -16,10 +12,12 @@ module Api
end
rescue_from ActiveRecord::RecordInvalid do
render json: I18n.t("api.endpoint_errors.likes.user_not_allowed_to_like"), status: :not_found
render json: I18n.t("api.endpoint_errors.likes.user_not_allowed_to_like"), status: :unprocessable_entity
end
def show
post = post_service.find!(params[:post_id])
raise ActiveRecord::RecordInvalid unless post.public? || private_read?
likes_query = like_service.find_for_post(params[:post_id])
likes_page = index_pager(likes_query).response
likes_page[:data] = likes_page[:data].map {|x| like_json(x) }
@ -27,6 +25,8 @@ module Api
end
def create
post = post_service.find!(params[:post_id])
raise ActiveRecord::RecordInvalid unless post.public? || private_modify?
like_service.create(params[:post_id])
rescue ActiveRecord::RecordInvalid => e
return render json: I18n.t("api.endpoint_errors.likes.like_exists"), status: :unprocessable_entity if
@ -37,6 +37,8 @@ module Api
end
def destroy
post = post_service.find!(params[:post_id])
raise ActiveRecord::RecordInvalid unless post.public? || private_modify?
success = like_service.unlike_post(params[:post_id])
if success
head :no_content
@ -45,11 +47,15 @@ module Api
end
end
private
def like_service
@like_service ||= LikeService.new(current_user)
end
private
def post_service
@post_service ||= PostService.new(current_user)
end
def like_json(like)
LikesPresenter.new(like).as_api_json

View file

@ -3,12 +3,8 @@
module Api
module V1
class MessagesController < Api::V1::BaseController
before_action only: %i[create] do
require_access_token %w[read write]
end
before_action only: %i[index] do
require_access_token %w[read]
before_action do
require_access_token %w[conversations]
end
rescue_from ActiveRecord::RecordNotFound do

View file

@ -3,12 +3,8 @@
module Api
module V1
class NotificationsController < Api::V1::BaseController
before_action except: %i[update] do
require_access_token %w[read]
end
before_action only: %i[update] do
require_access_token %w[write]
before_action do
require_access_token %w[notifications]
end
rescue_from ActiveRecord::RecordNotFound do

View file

@ -4,11 +4,11 @@ module Api
module V1
class PhotosController < Api::V1::BaseController
before_action except: %i[create destroy] do
require_access_token %w[read]
require_access_token %w[public:read]
end
before_action only: %i[create destroy] do
require_access_token %w[read write]
require_access_token %w[public:modify]
end
rescue_from ActiveRecord::RecordNotFound do
@ -16,7 +16,12 @@ module Api
end
def index
photos_page = time_pager(current_user.photos).response
query = if private_read?
current_user.photos
else
current_user.photos.where(public: true)
end
photos_page = time_pager(query).response
photos_page[:data] = photos_page[:data].map {|photo| PhotoPresenter.new(photo).as_api_json(true) }
render json: photos_page
end
@ -24,11 +29,14 @@ module Api
def show
photo = photo_service.visible_photo(params.require(:id))
raise ActiveRecord::RecordNotFound unless photo
raise ActiveRecord::RecordNotFound unless photo.public? || private_read?
render json: PhotoPresenter.new(photo).as_api_json(true)
end
def create
image = params.require(:image)
public_photo = params.has_key?(:aspect_ids)
raise RuntimeError unless public_photo || private_modify?
base_params = params.permit(:aspect_ids, :pending, :set_profile_photo)
photo = photo_service.create_from_params_and_file(base_params, image)
raise RuntimeError unless photo
@ -40,6 +48,7 @@ module Api
def destroy
photo = current_user.photos.where(guid: params[:id]).first
raise ActiveRecord::RecordNotFound unless photo
raise ActiveRecord::RecordNotFound unless photo.public? || private_modify?
if current_user.retract(photo)
head :no_content
else

View file

@ -6,7 +6,7 @@ module Api
include PostsHelper
before_action do
require_access_token %w[read write]
require_access_token %w[public:read interactions]
end
rescue_from ActiveRecord::RecordNotFound do
@ -14,7 +14,7 @@ module Api
end
def subscribe
post = post_service.find!(params[:post_id])
post = find_post
current_user.participate!(post)
head :no_content
rescue ActiveRecord::RecordInvalid
@ -22,13 +22,13 @@ module Api
end
def hide
post = post_service.find!(params[:post_id])
post = find_post
current_user.toggle_hidden_shareable(post)
head :no_content
end
def mute
post = post_service.find!(params[:post_id])
post = find_post
participation = current_user.participations.find_by!(target_id: post.id)
participation.destroy
head :no_content
@ -36,7 +36,7 @@ module Api
def report
reason = params.require(:reason)
post = post_service.find!(params[:post_id])
post = find_post
report = current_user.reports.new(
item_id: post.id,
item_type: "Post",
@ -53,7 +53,7 @@ module Api
def vote
begin
post = post_service.find!(params[:post_id])
post = find_post
rescue ActiveRecord::RecordNotFound
render json: I18n.t("api.endpoint_errors.posts.post_not_found"), status: :not_found
return
@ -77,6 +77,12 @@ module Api
def poll_service
@poll_service ||= PollParticipationService.new(current_user)
end
def find_post
post = post_service.find!(params[:post_id])
raise ActiveRecord::RecordNotFound unless post.public? || private_read?
post
end
end
end
end

View file

@ -5,12 +5,12 @@ module Api
class PostsController < Api::V1::BaseController
include PostsHelper
before_action only: :show do
require_access_token %w[read]
before_action except: %i[create destroy] do
require_access_token %w[public:read]
end
before_action only: %i[create destroy] do
require_access_token %w[read write]
require_access_token %w[public:modify]
end
rescue_from ActiveRecord::RecordNotFound do
@ -18,14 +18,13 @@ module Api
end
def show
mark_notifications =
params[:mark_notifications].present? && params[:mark_notifications]
post = post_service.find!(params[:id])
post_service.mark_user_notifications(post.id) if mark_notifications
raise ActiveRecord::RecordNotFound unless post.public? || private_read?
render json: post_as_json(post)
end
def create
raise StandardError unless params.require(:public) || private_modify?
status_service = StatusMessageCreationService.new(current_user)
creation_params = normalized_create_params
@status_message = status_service.create(creation_params)
@ -35,9 +34,9 @@ module Api
end
def destroy
post_service.destroy(params[:id])
post_service.destroy(params[:id], private_modify?)
head :no_content
rescue Diaspora::NotMine
rescue Diaspora::NotMine, Diaspora::NonPublic
render json: I18n.t("api.endpoint_errors.posts.failed_delete"), status: :forbidden
end

View file

@ -3,12 +3,12 @@
module Api
module V1
class ResharesController < Api::V1::BaseController
before_action only: %i[show] do
require_access_token %w[read]
before_action except: %i[create] do
require_access_token %w[public:read]
end
before_action only: %i[create] do
require_access_token %w[read write]
require_access_token %w[public:modify]
end
rescue_from ActiveRecord::RecordNotFound do

View file

@ -4,7 +4,7 @@ module Api
module V1
class SearchController < Api::V1::BaseController
before_action do
require_access_token %w[read]
require_access_token %w[public:read]
end
rescue_from ActionController::ParameterMissing, RuntimeError do
@ -15,17 +15,27 @@ module Api
parameters = params.permit(:tag, :name_or_handle)
raise RuntimeError if parameters.keys.length != 1
people_query = if params.has_key?(:tag)
Person.profile_tagged_with(params[:tag])
else
Person.search(params[:name_or_handle], current_user)
end
Person.profile_tagged_with(params[:tag])
else
connected_only = !private_read?
Person.search(
params[:name_or_handle],
current_user,
only_contacts: connected_only,
mutual: connected_only
)
end
user_page = index_pager(people_query).response
user_page[:data] = user_page[:data].map {|p| PersonPresenter.new(p).as_api_json }
render json: user_page
end
def post_index
posts_query = Stream::Tag.new(current_user, params.require(:tag)).posts
posts_query = if private_read?
Stream::Tag.new(current_user, params.require(:tag)).posts
else
Stream::Tag.new(nil, params.require(:tag)).posts
end
posts_page = time_pager(posts_query, "posts.created_at", "created_at").response
posts_page[:data] = posts_page[:data].map {|post| PostPresenter.new(post).as_api_response }
render json: posts_page

View file

@ -4,7 +4,15 @@ module Api
module V1
class StreamsController < Api::V1::BaseController
before_action do
require_access_token %w[read]
require_access_token %w[public:read]
end
before_action only: %w[aspects] do
require_access_token %w[contacts:read private:read]
end
before_action only: %w[followed_tags] do
require_access_token %w[tags:read]
end
def aspects
@ -41,7 +49,9 @@ module Api
def stream_responder(stream_klass=nil, query_time_field="posts.created_at", data_time_field="created_at")
@stream = stream_klass.present? ? stream_klass.new(current_user, max_time: stream_max_time) : @stream
posts_page = pager(@stream.stream_posts, query_time_field, data_time_field).response
query = @stream.stream_posts
query = query.where(public: true) unless private_read?
posts_page = pager(query, query_time_field, data_time_field).response
posts_page[:data] = posts_page[:data].map {|post| PostPresenter.new(post, current_user).as_api_response }
posts_page[:links].delete(:previous)
render json: posts_page

View file

@ -3,16 +3,12 @@
module Api
module V1
class TagFollowingsController < Api::V1::BaseController
before_action only: %i[index] do
require_access_token %w[read]
before_action except: %i[create destroy] do
require_access_token %w[tags:read]
end
before_action only: %i[create destroy] do
require_access_token %w[read write]
end
rescue_from StandardError do
render json: I18n.t("api.endpoint_errors.tags.cant_process"), status: :bad_request
require_access_token %w[tags:modify]
end
def index
@ -22,6 +18,8 @@ module Api
def create
tag_followings_service.create(params.require(:name))
head :no_content
rescue StandardError
render json: I18n.t("api.endpoint_errors.tags.cant_process"), status: :unprocessable_entity
end
def destroy

View file

@ -5,12 +5,20 @@ module Api
class UsersController < Api::V1::BaseController
include TagsHelper
before_action except: %i[update] do
require_access_token %w[read]
before_action except: %i[contacts update show] do
require_access_token %w[public:read]
end
before_action only: %i[update] do
require_access_token %w[write]
require_access_token %w[profile:modify]
end
before_action only: %i[contacts] do
require_access_token %w[contacts:read]
end
before_action only: %i[show] do
require_access_token %w[profile]
end
rescue_from ActiveRecord::RecordNotFound do
@ -19,7 +27,9 @@ module Api
def show
person = if params.has_key?(:id)
Person.find_by!(guid: params[:id])
found_person = Person.find_by!(guid: params[:id])
raise ActiveRecord::RecordNotFound unless found_person.searchable || access_token?("contacts:read")
found_person
else
current_user.person
end
@ -52,7 +62,8 @@ module Api
def photos
person = Person.find_by!(guid: params[:user_id])
photos_query = Photo.visible(current_user, person, :all, Time.current)
user_for_query = current_user if private_read?
photos_query = Photo.visible(user_for_query, person, :all, Time.current)
photos_page = time_pager(photos_query).response
photos_page[:data] = photos_page[:data].map {|photo| PhotoPresenter.new(photo).as_api_json(true) }
render json: photos_page
@ -60,7 +71,11 @@ module Api
def posts
person = Person.find_by!(guid: params[:user_id])
posts_query = current_user.posts_from(person, false)
posts_query = if private_read?
current_user.posts_from(person, false)
else
Post.where(author_id: person.id, public: true)
end
posts_page = time_pager(posts_query).response
posts_page[:data] = posts_page[:data].map {|post| PostPresenter.new(post, current_user).as_api_response }
render json: posts_page

View file

@ -18,7 +18,32 @@ module Api
scope :with_redirect_uri, ->(given_uri) { where redirect_uri: given_uri }
SCOPES = %w(openid sub aud name nickname profile picture read write)
SCOPES = %w[
birthdate
contacts:modify
contacts:read
conversations
email
gender
interactions
locale
name
nickname
notifications
openid
picture
private:modify
private:read
profile
profile
profile:modify
public:modify
public:read
sub
tags:modify
tags:read
updated_at
].freeze
def setup
self.refresh_token = SecureRandom.hex(32)

View file

@ -35,8 +35,12 @@ class PostService
mark_mention_notifications_read(post_id)
end
def destroy(post_id)
post = find!(post_id)
def destroy(post_id, private_allowed=true)
post = if private_allowed
find_non_public_by_guid_or_id_with_user!(post_id)
else
find_public!(post_id)
end
raise Diaspora::NotMine unless post.author == user.person
user.retract(post)
end

View file

@ -905,6 +905,8 @@ en:
deny: "Deny"
bad_request: "Missing client id or redirect URI"
client_id_not_found: "No client with client_id %{client_id} with redirect URI %{redirect_uri} found"
private_contacts_linkage_error: "private:read and private:modify require contacts:read as well"
unknown_scope: "Unknown scope: #{scope_name}"
destroy:
fail: "The attempt to revoke the authorization with ID %{id} failed"
user_applications:
@ -920,31 +922,73 @@ en:
scopes:
openid:
name: "basic profile"
description: "This allows the application to read your basic profile"
description: "This grants read access to basic profile information to the application."
sub:
name: "sub"
description: "This grants sub permissions to the application"
aud:
name: "aud"
description: "This grants aud permissions to the application"
description: "This grants sub permissions to the application."
name:
name: "name"
description: "This grants name permissions to the application"
description: "This grants read access to name data to the application."
nickname:
name: "nickname"
description: "This grants nickname permissions to the application"
profile:
name: "extended profile"
description: "This allows the application to read your extended profile"
description: "This grants read access to nickname data to the application."
picture:
name: "picture"
description: "This grants picture permissions to the application"
read:
name: "read profile, stream and conversations"
description: "This allows the application to read your stream, your conversations and your complete profile"
write:
name: "send posts, conversations and reactions"
description: "This allows the application to send new posts, write conversations, and send reactions"
description: "This grants read access to user's profile picture data to the application."
gender:
name: "gender"
description: "This grants read access to the user's gendere data to the application."
birthdate:
name: "birthdate"
description: "This grants read access ot the user's birthdate to the application."
locale:
name: "locale"
description: "This grants read access ot the user's locale to the application."
updated_at:
name: "updated_at"
description: "This grants read access to the user's profile update time to the application."
'contacts:read':
name: "contacts:read"
description: "This grants read permissions to contacts and related data (like aspects) to the application."
'contacts:modify':
name: "contacts:modify"
description: "This grants write permissions to contacts and related data (like aspects) to the application."
conversations:
name: "conversations"
description: "This grants read/write permission to private messaging to the application."
email:
name: "email"
description: "This grants read access to the user's email address to the application."
interactions:
name: "interactions"
description: "This grants read and write permissions on interacting wth posts to the application."
notifications:
name: "notifications"
description: "This grants read and write permissions on interactions for the user to the application."
'private:read':
name: "private:read"
description: "This grants read privileges on private posts and streams to the application."
'private:modify':
name: "private:modify"
description: "This grants modification privileges on private posts and streams, including allowing private sharing, to the application."
'public:read':
name: "public:read"
description: "This grants read permissions on public posts, interactions, and user data to the application."
'public:modify':
name: "updated_at"
description: "This grants write permissions on public posts (including public post writing), interactions, and user data to the application."
profile:
name: "extended profile"
description: "This grants read access to extendend profile data to the application."
'profile:modify':
name: "profile:modify"
description: "This grants user profile update permissions to the application."
'tags:read':
name: "tags:read"
description: "This grants read access to user's followed tags and tag streams to the application."
'tags:modify':
name: "tags:modify"
description: "This grants modification access to user's followed tags to the application."
error_page:
title: "Oh! Something went wrong :("
contact_developer: "You should contact the developer of the application and include the following detailed error message:"

View file

@ -50,9 +50,16 @@ module Api
replace_profile_scope_with_specific_claims(req)
@scopes = req.scope.map {|scope|
scope.tap do |scope_name|
req.invalid_scope! "Unknown scope: #{scope_name}" unless auth_scopes.include? scope_name
req.invalid_scope! I18n.t("api.openid_connect.authorizations.new.unknown_scope") \
unless auth_scopes.include?(scope_name)
end
}
@scopes.push("public:read") unless @scopes.include?("public:read")
has_private_scope = @scopes.include?("private:read") || @scopes.include?("private:modify")
has_contacts_scope = @scopes.include? "contacts:read"
req.invalid_scope! I18n.t("api.openid_connect.authorizations.new.private_contacts_linkage_error") \
if has_private_scope && !has_contacts_scope
end
def auth_scopes

View file

@ -17,7 +17,7 @@ module Api
end
def replace_profile_scope_with_specific_claims(req)
profile_claims = %w(sub aud name nickname profile picture)
profile_claims = %w[sub name nickname profile picture gender birthdate locale updated_at]
scopes_as_claims = req.scope.flat_map {|scope| scope == "profile" ? profile_claims : [scope] }.uniq
req.update_param("scope", scopes_as_claims)
end

View file

@ -29,11 +29,15 @@ module Api
attr_reader :current_token
def require_access_token(required_scopes)
raise Rack::OAuth2::Server::Resource::Bearer::Forbidden.new(:insufficient_scope) unless
access_token?(required_scopes)
end
def access_token?(required_scopes)
@current_token = request.env[Rack::OAuth2::Server::Resource::ACCESS_TOKEN]
raise Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new("Unauthorized user") unless
@current_token && @current_token.authorization
raise Rack::OAuth2::Server::Resource::Bearer::Forbidden.new(:insufficient_scope) unless
@current_token.authorization.try(:accessible?, required_scopes)
@current_token.authorization.try(:accessible?, required_scopes)
end
end
end

View file

@ -27,6 +27,7 @@ module Api
auth = Api::OpenidConnect::Authorization.with_redirect_uri(req.redirect_uri).use_code(req.code)
req.invalid_grant! if auth.blank?
res.access_token = auth.create_access_token
res.access_token.refresh_token = auth.refresh_token
if auth.accessible? "openid"
id_token = auth.create_id_token
res.id_token = id_token.to_jwt(access_token: res.access_token)

View file

@ -253,7 +253,7 @@ describe Api::OpenidConnect::AuthorizationsController, type: :request do
before do
get new_api_openid_connect_authorization_path, params: {client_id: client.client_id,
redirect_uri: "http://localhost:3000/", response_type: "id_token",
scope: "openid read", nonce: 413_093_098_3, state: 413_093_098_3}
scope: "openid interactions", nonce: 413_093_098_3, state: 413_093_098_3}
end
it "should receive another authorization request" do
@ -380,12 +380,71 @@ describe Api::OpenidConnect::AuthorizationsController, type: :request do
end
end
end
context "ensure public:read always there" do
it "added with only openid" do
get new_api_openid_connect_authorization_path, params: {client_id: client.client_id,
redirect_uri: "http://localhost:3000/",
response_type: "id_token token",
scope: "openid",
nonce: 418_093_098_3,
state: 418_093_098_3}
post api_openid_connect_authorizations_path, params: {approve: "true"}
expect(alice.reload.authorizations.first.scopes).to include("public:read")
end
end
context "with contacts:read and private linkage requirement" do
it "fails without contacts:read on private:read" do
scopes = "openid private:read"
get new_api_openid_connect_authorization_path, params: {client_id: client.client_id,
redirect_uri: "http://localhost:3000/",
response_type: "id_token token",
scope: scopes,
nonce: 418_093_098_3,
state: 418_093_098_3}
expect(response.status).to eq(302)
end
it "fails without contacts:read on private:modify" do
scopes = "openid private:modify"
get new_api_openid_connect_authorization_path, params: {client_id: client.client_id,
redirect_uri: "http://localhost:3000/",
response_type: "id_token token",
scope: scopes,
nonce: 418_093_098_3,
state: 418_093_098_3}
expect(response.status).to eq(302)
end
it "succeeds with contacts:read on private:read" do
scopes = "openid contacts:read private:read"
get new_api_openid_connect_authorization_path, params: {client_id: client.client_id,
redirect_uri: "http://localhost:3000/",
response_type: "id_token token",
scope: scopes,
nonce: 418_093_098_3,
state: 418_093_098_3}
expect(response.status).to eq(200)
end
it "succeeds with contacts:read on private:modify" do
scopes = "openid contacts:read private:modify"
get new_api_openid_connect_authorization_path, params: {client_id: client.client_id,
redirect_uri: "http://localhost:3000/",
response_type: "id_token token",
scope: scopes,
nonce: 418_093_098_3,
state: 418_093_098_3}
expect(response.status).to eq(200)
end
end
end
describe "#destroy" do
context "with existent authorization" do
it "removes the authorization" do
auth_with_read = FactoryGirl.create(:auth_with_read, o_auth_application: client)
auth_with_read = FactoryGirl.create(:auth_with_profile_only, o_auth_application: client)
delete api_openid_connect_authorization_path(auth_with_read.id)
expect(Api::OpenidConnect::Authorization.find_by(id: auth_with_read.id)).to be_nil
end

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
describe Api::OpenidConnect::TokenEndpointController, type: :controller, suppress_csrf_verification: :none do
let(:auth) { FactoryGirl.create(:auth_with_read) }
let(:auth) { FactoryGirl.create(:auth_with_profile_only) }
describe "#create" do
it "returns 200 on success" do
@ -15,5 +15,16 @@ describe Api::OpenidConnect::TokenEndpointController, type: :controller, suppres
}
expect(response.code).to eq("200")
end
it "refresh returns 200 on success" do
post :create, params: {
grant_type: "refresh_token",
refresh_token: auth.refresh_token,
client_id: auth.o_auth_application.client_id,
client_secret: auth.o_auth_application.client_secret
}
expect(response.code).to eq("200")
puts response.body
end
end
end

View file

@ -4,7 +4,7 @@ describe Api::OpenidConnect::UserApplicationsController, type: :controller do
before do
@app = FactoryGirl.create(:o_auth_application_with_xss)
@user = FactoryGirl.create :user
FactoryGirl.create :auth_with_read, user: @user, o_auth_application: @app
FactoryGirl.create :auth_with_profile_only, user: @user, o_auth_application: @app
sign_in @user, scope: :user
end

View file

@ -403,28 +403,59 @@ FactoryGirl.define do
redirect_uris %w(http://localhost:3000/)
end
factory :auth_with_read, class: Api::OpenidConnect::Authorization do
factory :auth_with_profile_only, class: Api::OpenidConnect::Authorization do
o_auth_application
user
scopes %w(openid sub aud profile picture nickname name read)
scopes %w[openid profile]
after(:build) {|m|
m.redirect_uri = m.o_auth_application.redirect_uris[0]
}
end
factory :auth_with_read_and_ppid, class: Api::OpenidConnect::Authorization do
factory :auth_with_profile_and_ppid, class: Api::OpenidConnect::Authorization do
association :o_auth_application, factory: :o_auth_application_with_ppid
user
scopes %w(openid sub aud profile picture nickname name read)
scopes %w[openid sub profile picture nickname name]
after(:build) {|m|
m.redirect_uri = m.o_auth_application.redirect_uris[0]
}
end
factory :auth_with_read_and_write, class: Api::OpenidConnect::Authorization do
factory :auth_with_all_scopes, class: Api::OpenidConnect::Authorization do
o_auth_application
association :user, factory: :user_with_aspect
scopes %w(openid sub aud profile picture nickname name read write)
scopes Api::OpenidConnect::Authorization::SCOPES
after(:build) {|m|
m.redirect_uri = m.o_auth_application.redirect_uris[0]
}
end
factory :auth_with_all_scopes_not_private, class: Api::OpenidConnect::Authorization do
o_auth_application
association :user, factory: :user_with_aspect
scopes %w[openid sub name nickname profile picture gender birthdate locale updated_at contacts:read contacts:modify
conversations email interactions notifications public:read public:modify profile profile:modify tags:read
tags:modify]
after(:build) {|m|
m.redirect_uri = m.o_auth_application.redirect_uris[0]
}
end
factory :auth_with_read_scopes, class: Api::OpenidConnect::Authorization do
o_auth_application
association :user, factory: :user_with_aspect
scopes %w[openid sub name nickname profile picture gender birthdate locale updated_at contacts:read conversations
email interactions notifications private:read public:read profile tags:read]
after(:build) {|m|
m.redirect_uri = m.o_auth_application.redirect_uris[0]
}
end
factory :auth_with_read_scopes_not_private, class: Api::OpenidConnect::Authorization do
o_auth_application
association :user, factory: :user_with_aspect
scopes %w[openid sub name nickname profile picture gender birthdate locale updated_at contacts:read conversations
email interactions notifications public:read profile tags:read]
after(:build) {|m|
m.redirect_uri = m.o_auth_application.redirect_uris[0]
}

View file

@ -3,14 +3,31 @@
require "spec_helper"
describe Api::V1::AspectsController do
let(:auth) { FactoryGirl.create(:auth_with_read_and_write) }
let(:auth_read_only) { FactoryGirl.create(:auth_with_read) }
let(:auth) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid contacts:read contacts:modify]
)
}
let(:auth_read_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid contacts:read]
)
}
let(:auth_profile_only) {
FactoryGirl.create(:auth_with_profile_only)
}
let!(:access_token) { auth.create_access_token.to_s }
let!(:access_token_read_only) { auth_read_only.create_access_token.to_s }
let!(:access_token_profile_only) { auth_profile_only.create_access_token.to_s }
before do
@aspect1 = auth.user.aspects.where(name: "generic").first
@aspect2 = auth.user.aspects.create(name: "another aspect")
@aspect1 = auth.user.aspects.create(name: "first aspect")
@aspect2 = auth.user.aspects.create(name: "second aspect")
end
describe "#index" do
@ -29,6 +46,14 @@ describe Api::V1::AspectsController do
end
end
it "fails if token doesn't have contacts:read" do
get(
api_v1_aspects_path,
params: {access_token: access_token_profile_only}
)
expect(response.status).to eq(403)
end
it "fails if invalid token" do
get(
api_v1_aspects_path,
@ -65,6 +90,16 @@ describe Api::V1::AspectsController do
end
end
context "without contacts:read in token" do
it "fails to return with error" do
get(
api_v1_aspect_path(@aspect2.id),
params: {access_token: access_token_profile_only}
)
expect(response.status).to eq(403)
end
end
context "when not logged in" do
it "fails to return with error" do
get(

View file

@ -3,8 +3,22 @@
require "spec_helper"
describe Api::V1::CommentsController do
let(:auth) { FactoryGirl.create(:auth_with_read_and_write) }
let(:auth) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read public:modify private:read private:modify interactions]
)
}
let(:auth_public_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read public:modify interactions]
)
}
let!(:access_token) { auth.create_access_token.to_s }
let!(:access_token_public_only) { auth_public_only.create_access_token.to_s }
before do
@status = alice.post(
@ -24,13 +38,27 @@ describe Api::V1::CommentsController do
)
@comment_on_eves_post = comment_service.create(@eves_post.guid, "Comment on eve's post")
aspect = auth_public_only.user.aspects.create(name: "first aspect")
@private_post = auth_public_only.user.post(
"Post",
status_message: {text: "This is a private status message"},
public: false,
to: [aspect.id],
type: "Post"
)
@comment_on_private_post = comment_service(auth_public_only.user).create(@private_post.guid, "Private comment")
end
describe "#create" do
context "valid post ID" do
it "succeeds in adding a comment" do
comment_text = "This is a comment"
create_comment(@status.guid, comment_text)
post(
api_v1_post_comments_path(post_id: @status.guid),
params: {body: comment_text, access_token: access_token}
)
expect(response.status).to eq(201)
comment = response_body(response)
confirm_comment_format(comment, auth.user, comment_text)
@ -39,7 +67,10 @@ describe Api::V1::CommentsController do
context "wrong post id" do
it "fails at adding a comment" do
create_comment("999_999_999", "This is a comment")
post(
api_v1_post_comments_path(post_id: "999_999_999"),
params: {body: "text", access_token: access_token}
)
expect(response.status).to eq(404)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.post_not_found"))
end
@ -48,9 +79,11 @@ describe Api::V1::CommentsController do
context "lack of permissions" do
it "fails at adding a comment" do
alice.blocks.create(person: auth.user.person)
create_comment(@status.guid, "That shouldn't be there because I am ignored by this user")
post(
api_v1_post_comments_path(post_id: @status.guid),
params: {body: "That shouldn't be there because I am ignored by this user", access_token: access_token}
)
expect(response.status).to eq(422)
expect(response.body).to eq(I18n.t("api.endpoint_errors.comments.not_allowed"))
end
end
end
@ -59,8 +92,8 @@ describe Api::V1::CommentsController do
before do
@comment_text1 = "This is a comment"
@comment_text2 = "This is a comment 2"
create_comment(@status.guid, @comment_text1)
create_comment(@status.guid, @comment_text2)
comment_service.create(@status.guid, @comment_text1)
comment_service.create(@status.guid, @comment_text2)
end
context "valid post ID" do
@ -87,12 +120,23 @@ describe Api::V1::CommentsController do
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.post_not_found"))
end
end
context "can't see comment on limited post without private:read token" do
it "fails" do
get(
api_v1_post_comments_path(post_id: @private_post.guid),
params: {access_token: access_token_public_only}
)
expect(response.status).to eq(404)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.post_not_found"))
end
end
end
describe "#delete" do
before do
create_comment(@status.guid, "This is a comment")
@comment_guid = response_body(response)["guid"]
comment = comment_service.create(@status.guid, "This is a comment")
@comment_guid = comment.guid
end
context "valid comment ID" do
@ -152,8 +196,8 @@ describe Api::V1::CommentsController do
end
end
context "insufficient permissions (not your comment and not owner)" do
it "fails at deleting comment" do
context "insufficient permissions" do
it "fails at deleting other user's comment on other user's post" do
alices_comment = comment_service(alice).create(@status.guid, "Alice's comment")
delete(
api_v1_post_comment_path(
@ -165,13 +209,24 @@ describe Api::V1::CommentsController do
expect(response.status).to eq(403)
expect(response.body).to eq(I18n.t("api.endpoint_errors.comments.no_delete"))
end
it "fails at deleting your comment on post without private:modify token" do
delete(
api_v1_post_comment_path(
post_id: @private_post.guid,
id: @comment_on_private_post.guid
),
params: {access_token: access_token_public_only}
)
expect(response.status).to eq(404)
end
end
end
describe "#report" do
before do
create_comment(@status.guid, "This is a comment")
@comment_guid = response_body(response)["guid"]
comment = comment_service.create(@status.guid, "This is a comment")
@comment_guid = comment.guid
end
context "valid comment ID" do
@ -227,6 +282,40 @@ describe Api::V1::CommentsController do
end
end
context "invalid Post ID" do
it "fails at reporting comment" do
post(
api_v1_post_comment_report_path(
post_id: "999_999_999",
comment_id: @comment_guid
),
params: {
reason: "bad comment",
access_token: access_token
}
)
expect(response.status).to eq(404)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.post_not_found"))
end
end
context "lack of private permissions on private post" do
it "fails at reporting comment" do
post(
api_v1_post_comment_report_path(
post_id: @private_post.guid,
comment_id: @comment_on_private_post.guid
),
params: {
reason: "bad comment",
access_token: access_token_public_only
}
)
expect(response.status).to eq(404)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.post_not_found"))
end
end
context "mismatched post-to-comment ID" do
it "fails at reporting comment" do
post(
@ -278,13 +367,6 @@ describe Api::V1::CommentsController do
CommentService.new(user)
end
def create_comment(post_guid, text)
post(
api_v1_post_comments_path(post_id: post_guid),
params: {body: text, access_token: access_token}
)
end
def response_body(response)
JSON.parse(response.body)
end

View file

@ -3,13 +3,31 @@
require "spec_helper"
describe Api::V1::ContactsController do
let(:auth) { FactoryGirl.create(:auth_with_read_and_write) }
let(:auth_read_only) { FactoryGirl.create(:auth_with_read) }
let(:auth) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid contacts:read contacts:modify]
)
}
let(:auth_read_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid contacts:read]
)
}
let(:auth_profile_only) {
FactoryGirl.create(:auth_with_profile_only)
}
let!(:access_token) { auth.create_access_token.to_s }
let!(:access_token_read_only) { auth_read_only.create_access_token.to_s }
let!(:access_token_profile_only) { auth_profile_only.create_access_token.to_s }
before do
@aspect1 = auth.user.aspects.where(name: "generic").first
@aspect1 = auth.user.aspects.create(name: "generic")
auth.user.share_with(eve.person, @aspect1)
@aspect2 = auth.user.aspects.create(name: "another aspect")
@eve_aspect = eve.aspects.first
end

View file

@ -3,12 +3,28 @@
require "spec_helper"
describe Api::V1::ConversationsController do
let(:auth) { FactoryGirl.create(:auth_with_read_and_write) }
let(:auth) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid conversations]
)
}
let(:auth_participant) {
FactoryGirl.create(:auth_with_all_scopes)
}
let(:auth_profile_only) {
FactoryGirl.create(:auth_with_profile_only)
}
let!(:access_token) { auth.create_access_token.to_s }
let(:auth_participant) { FactoryGirl.create(:auth_with_read_and_write) }
let!(:access_token_participant) { auth_participant.create_access_token.to_s }
let!(:access_token_profile_only) { auth_profile_only.create_access_token.to_s }
before do
auth.user.aspects.create(name: "first")
auth.user.share_with alice.person, auth.user.aspects[0]
alice.share_with auth.user.person, alice.aspects[0]
auth.user.disconnected_by(eve)

View file

@ -3,8 +3,27 @@
require "spec_helper"
describe Api::V1::LikesController do
let(:auth) { FactoryGirl.create(:auth_with_read_and_write) }
let(:auth) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read public:modify private:read private:modify interactions]
)
}
let(:auth_public_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read public:modify interactions]
)
}
let(:auth_profile_only) {
FactoryGirl.create(:auth_with_profile_only)
}
let!(:access_token) { auth.create_access_token.to_s }
let!(:access_token_public_only) { auth_public_only.create_access_token.to_s }
let!(:access_token_profile_only) { auth_profile_only.create_access_token.to_s }
before do
@status = auth.user.post(
@ -13,6 +32,15 @@ describe Api::V1::LikesController do
public: true,
to: "all"
)
aspect = auth_public_only.user.aspects.create(name: "first aspect")
@private_status = auth_public_only.user.post(
"Post",
status_message: {text: "This is a private status message"},
public: false,
to: [aspect.id],
type: "Post"
)
end
describe "#show" do
@ -54,6 +82,17 @@ describe Api::V1::LikesController do
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.post_not_found"))
end
end
context "without private:read scope in token" do
it "fails at getting likes" do
get(
api_v1_post_likes_path(post_id: @private_status.guid),
params: {access_token: access_token_public_only}
)
expect(response.status).to eq(422)
expect(response.body).to eq(I18n.t("api.endpoint_errors.likes.user_not_allowed_to_like"))
end
end
end
describe "#create" do
@ -87,6 +126,15 @@ describe Api::V1::LikesController do
expect(likes.length).to eq(1)
expect(likes[0].author.id).to eq(auth.user.person.id)
end
it "fails in liking private post without private:modify" do
post(
api_v1_post_likes_path(post_id: @private_status.guid),
params: {access_token: access_token_public_only}
)
expect(response.status).to eq(422)
expect(response.body).to eq(I18n.t("api.endpoint_errors.likes.user_not_allowed_to_like"))
end
end
context "with wrong post id" do
@ -103,10 +151,7 @@ describe Api::V1::LikesController do
describe "#delete" do
before do
post(
api_v1_post_likes_path(post_id: @status.guid),
params: {access_token: access_token}
)
like_service.create(@status.guid)
end
context "with right post id" do
@ -137,6 +182,16 @@ describe Api::V1::LikesController do
likes = like_service.find_for_post(@status.guid)
expect(likes.length).to eq(0)
end
it "fails at unliking private post without private:modify" do
like_service(auth_public_only.user).create(@private_status.guid)
delete(
api_v1_post_likes_path(post_id: @private_status.guid),
params: {access_token: access_token}
)
expect(response.status).to eq(404)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.post_not_found"))
end
end
context "with wrong post id" do

View file

@ -3,7 +3,13 @@
require "spec_helper"
describe Api::V1::MessagesController do
let(:auth) { FactoryGirl.create(:auth_with_read_and_write) }
let(:auth) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid conversations]
)
}
let!(:access_token) { auth.create_access_token.to_s }
before do

View file

@ -3,10 +3,16 @@
require "spec_helper"
describe Api::V1::NotificationsController do
let(:auth) { FactoryGirl.create(:auth_with_read_and_write) }
let(:auth_read_only) { FactoryGirl.create(:auth_with_read) }
let(:auth) {
FactoryGirl.create(:auth_with_all_scopes)
}
let(:auth_profile_only) {
FactoryGirl.create(:auth_with_profile_only)
}
let!(:access_token) { auth.create_access_token.to_s }
let!(:access_token_read_only) { auth_read_only.create_access_token.to_s }
let!(:access_token_profile_only) { auth_profile_only.create_access_token.to_s }
before do
@post = auth.user.post(
@ -78,6 +84,14 @@ describe Api::V1::NotificationsController do
expect(response.body).to eq(I18n.t("api.endpoint_errors.notifications.cant_process"))
end
it "with insufficient credentials" do
get(
api_v1_notifications_path,
params: {access_token: access_token_profile_only}
)
expect(response.status).to eq(403)
end
it "with improper credentials" do
get(
api_v1_notifications_path,
@ -128,6 +142,14 @@ describe Api::V1::NotificationsController do
expect(response.body).to eq(I18n.t("api.endpoint_errors.notifications.not_found"))
end
it "with insufficient credentials" do
get(
api_v1_notification_path(@notification.guid),
params: {access_token: access_token_profile_only}
)
expect(response.status).to eq(403)
end
it "with improper credentials" do
get(
api_v1_notification_path(@notification.guid),
@ -175,10 +197,10 @@ describe Api::V1::NotificationsController do
expect(response.body).to eq(I18n.t("api.endpoint_errors.notifications.cant_process"))
end
it "with read only credentials" do
it "with insufficient credentials" do
patch(
api_v1_notification_path(@notification.guid),
params: {access_token: access_token_read_only}
params: {access_token: access_token_profile_only}
)
expect(response.status).to eq(403)
end

View file

@ -3,10 +3,43 @@
require "spec_helper"
describe Api::V1::PhotosController do
let(:auth) { FactoryGirl.create(:auth_with_read_and_write) }
let(:auth_read_only) { FactoryGirl.create(:auth_with_read) }
let(:auth) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read public:modify private:read private:modify]
)
}
let(:auth_public_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read public:modify]
)
}
let(:auth_read_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read private:read]
)
}
let(:auth_public_only_read_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read]
)
}
let(:auth_profile_only) {
FactoryGirl.create(:auth_with_profile_only)
}
let!(:access_token) { auth.create_access_token.to_s }
let!(:access_token_public_only) { auth_public_only.create_access_token.to_s }
let!(:access_token_read_only) { auth_read_only.create_access_token.to_s }
let!(:access_token_public_only_read_only) { auth_public_only_read_only.create_access_token.to_s }
let!(:access_token_profile_only) { auth_profile_only.create_access_token.to_s }
before do
alice_private_spec = alice.aspects.create(name: "private aspect")
@ -19,6 +52,17 @@ describe Api::V1::PhotosController do
message_data = {status_message: {text: "Post with photos"}, public: true, photos: [@user_photo2.id.to_s]}
@status_message = StatusMessageCreationService.new(auth.user).create(message_data)
@user_photo2.reload
shared_spec = auth_public_only_read_only.user.aspects.create(name: "shared aspect")
auth_public_only_read_only.user.share_with(auth_public_only_read_only.user.person, shared_spec)
auth_public_only_read_only.user.share_with(auth_read_only.user.person, shared_spec)
@shared_photo1 = auth_public_only_read_only.user.post(
:photo,
pending: false,
user_file: File.open(photo_fixture_name),
to: shared_spec.id
)
end
describe "#show" do
@ -57,6 +101,15 @@ describe Api::V1::PhotosController do
end
context "fails" do
it "with other this user's private photo without private:read scope in token" do
get(
api_v1_photo_path(@shared_photo1.guid),
params: {access_token: access_token_public_only_read_only}
)
expect(response.status).to eq(404)
expect(response.body).to eq(I18n.t("api.endpoint_errors.photos.not_found"))
end
it "with other user's private photo" do
get(
api_v1_photo_path(@private_photo1.guid),
@ -98,6 +151,23 @@ describe Api::V1::PhotosController do
end
end
context "only lists public photos" do
before do
auth_public_only_read_only.user.post(:photo, pending: false, user_file: File.open(photo_fixture_name),
public: true)
end
it "with correct only public scope token" do
get(
api_v1_photos_path,
params: {access_token: access_token_public_only_read_only}
)
expect(response.status).to eq(200)
photos = response_body_data(response)
expect(photos.length).to eq(1)
end
end
context "fails" do
it "with invalid access token" do
delete(
@ -214,6 +284,14 @@ describe Api::V1::PhotosController do
)
expect(response.status).to eq(403)
end
it "with private photo and no private:modify access token" do
post(
api_v1_photos_path,
params: {image: @encoded_photo, access_token: access_token_public_only_read_only}
)
expect(response.status).to eq(403)
end
end
end
@ -264,6 +342,14 @@ describe Api::V1::PhotosController do
)
expect(response.status).to eq(403)
end
it "with private photo and no private:modify token" do
delete(
api_v1_photo_path(@shared_photo1.guid),
params: {access_token: access_token_public_only_read_only}
)
expect(response.status).to eq(403)
end
end
end

View file

@ -3,10 +3,27 @@
require "spec_helper"
describe Api::V1::PostInteractionsController do
let(:auth) { FactoryGirl.create(:auth_with_read_and_write) }
let(:auth) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read public:modify private:read private:modify interactions]
)
}
let(:auth_public_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read public:modify interactions]
)
}
let(:auth_profile_only) {
FactoryGirl.create(:auth_with_profile_only)
}
let!(:access_token) { auth.create_access_token.to_s }
let(:auth_read_only) { FactoryGirl.create(:auth_with_read) }
let!(:access_token_read_only) { auth_read_only.create_access_token.to_s }
let!(:access_token_public_only) { auth_public_only.create_access_token.to_s }
let!(:access_token_profile_only) { auth_profile_only.create_access_token.to_s }
before do
@status = alice.post(
@ -15,6 +32,13 @@ describe Api::V1::PostInteractionsController do
public: true,
to: "all"
)
alice_shared_aspect = alice.aspects.create(name: "shared aspect")
alice.share_with(auth_public_only.user.person, alice_shared_aspect)
alice.share_with(auth.user.person, alice_shared_aspect)
alice.share_with(auth_profile_only.user.person, alice_shared_aspect)
@shared_post = alice.post(:status_message, text: "to aspect only", public: false, to: alice_shared_aspect.id)
end
describe "#subscribe" do
@ -61,16 +85,26 @@ describe Api::V1::PostInteractionsController do
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.post_not_found"))
end
it "with read only token" do
it "with insufficient token" do
post(
api_v1_post_subscribe_path(@status.guid),
params: {
access_token: access_token_read_only
access_token: access_token_profile_only
}
)
expect(response.status).to eq(403)
end
it "on private post without private token" do
post(
api_v1_post_subscribe_path(@shared_post.guid),
params: {
access_token: access_token_public_only
}
)
expect(response.status).to eq(404)
end
it "with invalid token" do
post(
api_v1_post_subscribe_path(@status.guid),
@ -110,16 +144,26 @@ describe Api::V1::PostInteractionsController do
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.post_not_found"))
end
it "with read only token" do
it "with insufficient token" do
post(
api_v1_post_hide_path(@status.guid),
params: {
access_token: access_token_read_only
access_token: access_token_profile_only
}
)
expect(response.status).to eq(403)
end
it "on private post without private token" do
post(
api_v1_post_hide_path(@shared_post.guid),
params: {
access_token: access_token_public_only
}
)
expect(response.status).to eq(404)
end
it "with invalid token" do
post(
api_v1_post_hide_path(@status.guid),
@ -186,16 +230,26 @@ describe Api::V1::PostInteractionsController do
expect(response.status).to eq(404)
end
it "with read only token" do
it "with insufficient token" do
post(
api_v1_post_mute_path(@status.guid),
params: {
access_token: access_token_read_only
access_token: access_token_profile_only
}
)
expect(response.status).to eq(403)
end
it "on private post without private token" do
post(
api_v1_post_mute_path(@shared_post.guid),
params: {
access_token: access_token_public_only
}
)
expect(response.status).to eq(404)
end
it "with invalid token" do
post(
api_v1_post_mute_path(@status.guid),
@ -268,17 +322,28 @@ describe Api::V1::PostInteractionsController do
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.cant_report"))
end
it "with read only token" do
it "with insufficient token" do
post(
api_v1_post_report_path(@status.guid),
params: {
reason: "My reason",
access_token: access_token_read_only
access_token: access_token_profile_only
}
)
expect(response.status).to eq(403)
end
it "on private post without private token" do
post(
api_v1_post_report_path(@shared_post.guid),
params: {
reason: "My reason",
access_token: access_token_public_only
}
)
expect(response.status).to eq(404)
end
it "with invalid token" do
post(
api_v1_post_report_path(@status.guid),
@ -356,17 +421,29 @@ describe Api::V1::PostInteractionsController do
expect(response.status).to eq(404)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.post_not_found"))
end
it "with read only token" do
it "with insufficient token" do
post(
api_v1_post_vote_path(@poll_post.guid),
params: {
poll_answer_id: @poll_answer.id,
access_token: access_token_read_only
access_token: access_token_profile_only
}
)
expect(response.status).to eq(403)
end
it "on private post without private token" do
post(
api_v1_post_vote_path(@shared_post.guid),
params: {
poll_answer_id: @poll_answer.id,
access_token: access_token_public_only
}
)
expect(response.status).to eq(404)
end
it "with invalid token" do
post(
api_v1_post_vote_path(@poll_post.guid),

View file

@ -3,16 +3,41 @@
require "spec_helper"
describe Api::V1::PostsController do
let!(:auth_with_read) { FactoryGirl.create(:auth_with_read) }
let!(:access_token_with_read) { auth_with_read.create_access_token.to_s }
let(:auth) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read public:modify private:read private:modify]
)
}
let(:auth_with_read_and_write) {
FactoryGirl.create(:auth_with_read_and_write)
let(:auth_public_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read public:modify]
)
}
let!(:access_token_with_read_and_write) {
auth_with_read_and_write.create_access_token.to_s
let(:auth_read_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read private:read]
)
}
let(:auth_public_only_read_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read]
)
}
let(:auth_profile_only) { FactoryGirl.create(:auth_with_profile_only) }
let!(:access_token) { auth.create_access_token.to_s }
let!(:access_token_public_only) { auth_public_only.create_access_token.to_s }
let!(:access_token_read_only) { auth_read_only.create_access_token.to_s }
let!(:access_token_public_only_read_only) { auth_public_only_read_only.create_access_token.to_s }
let!(:access_token_profile_only) { auth_profile_only.create_access_token.to_s }
let(:alice_aspect) { alice.aspects.first }
let(:alice_photo1) {
@ -36,87 +61,12 @@ describe Api::V1::PostsController do
)
end
context "when mark notifications is omitted" do
# TODO: Determine if this needs to be added back to the spec or if this is just in the notifications services
xit "shows attempts to show the info and mark the user notifications" do
@status = auth_with_read.user.post(
:status_message,
text: "hello @{bob Testing ; bob@example.com}",
public: true,
to: "all"
)
get(
api_v1_post_path(@status.id),
params: {access_token: access_token_with_read}
)
expect(response.status).to eq(200)
post = response_body(response)
expect(post["post_type"]).to eq("StatusMessage")
expect(post["public"]).to eq(true)
expect(post["author"]["id"]).to eq(auth_with_read.user.person.id)
expect(post["interactions"]["comments_count"]).to eq(0)
mention_ids = Mention.where(
mentions_container_id: @status.id,
mentions_container_type: "Post",
person_id: bob.person.id
).ids
Notification.where(
recipient_id: bob.person.id,
target_type: "Mention",
target_id: mention_ids,
unread: true
)
# expect(notifications.length).to eq(0)
end
end
context "when mark notifications is false" do
# TODO: Determine if this needs to be added back to the spec or if this is just in the notifications services
xit "shows attempts to show the info" do
@status = auth_with_read.user.post(
:status_message,
text: "hello @{bob ; bob@example.com}",
public: true,
to: "all"
)
get(
api_v1_post_path(@status.id),
params: {
access_token: access_token_with_read,
mark_notifications: "false"
}
)
expect(response.status).to eq(200)
post = response_body(response)
expect(post["post_type"]).to eq("StatusMessage")
expect(post["public"]).to eq(true)
expect(post["author"]["id"]).to eq(auth_with_read.user.person.id)
expect(post["interactions"]["comments_count"]).to eq(0)
mention_ids = Mention.where(
mentions_container_id: @status.id,
mentions_container_type: "Post",
person_id: bob.person.id
).ids
Notification.where(
recipient_id: bob.person.id,
target_type: "Mention",
target_id: mention_ids,
unread: true
)
# expect(notifications.length).to eq(1)
end
end
context "access simple by post ID" do
it "gets post" do
get(
api_v1_post_path(@status.id),
api_v1_post_path(@status.guid),
params: {
access_token: access_token_with_read
access_token: access_token
}
)
expect(response.status).to eq(200)
@ -136,9 +86,9 @@ describe Api::V1::PostsController do
status_message = StatusMessageCreationService.new(alice).create(merged_params)
get(
api_v1_post_path(status_message.id),
api_v1_post_path(status_message.guid),
params: {
access_token: access_token_with_read
access_token: access_token
}
)
expect(response.status).to eq(200)
@ -151,9 +101,9 @@ describe Api::V1::PostsController do
it "gets post" do
reshare_post = FactoryGirl.create(:reshare, root: @status, author: bob.person)
get(
api_v1_post_path(reshare_post.id),
api_v1_post_path(reshare_post.guid),
params: {
access_token: access_token_with_read
access_token: access_token
}
)
expect(response.status).to eq(200)
@ -166,9 +116,9 @@ describe Api::V1::PostsController do
it "fails to get post" do
private_post = alice.post(:status_message, text: "to aspect only", public: false, to: alice.aspects.first.id)
get(
api_v1_post_path(private_post.id),
api_v1_post_path(private_post.guid),
params: {
access_token: access_token_with_read
access_token: access_token
}
)
expect(response.status).to eq(404)
@ -176,12 +126,38 @@ describe Api::V1::PostsController do
end
end
context "access private post to reader without private:read scope in token" do
it "fails to get post" do
alice_shared_aspect = alice.aspects.create(name: "shared aspect")
alice.share_with(auth_public_only_read_only.user.person, alice_shared_aspect)
alice.share_with(auth_read_only.user.person, alice_shared_aspect)
shared_post = alice.post(:status_message, text: "to aspect only", public: false, to: alice_shared_aspect.id)
get(
api_v1_post_path(shared_post.guid),
params: {
access_token: access_token_public_only_read_only
}
)
expect(response.status).to eq(404)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.post_not_found"))
get(
api_v1_post_path(shared_post.guid),
params: {
access_token: access_token_read_only
}
)
expect(response.status).to eq(200)
end
end
context "access post with invalid id" do
it "fails to get post" do
get(
api_v1_post_path("999_999_999"),
params: {
access_token: access_token_with_read
access_token: access_token
}
)
expect(response.status).to eq(404)
@ -192,11 +168,11 @@ describe Api::V1::PostsController do
describe "#create" do
let(:user_photo1) {
auth_with_read_and_write.user.build_post(:photo, pending: true,
auth.user.build_post(:photo, pending: true,
user_file: File.open(photo_fixture_name), to: "all").tap(&:save!)
}
let(:user_photo2) {
auth_with_read_and_write.user.build_post(:photo, pending: true,
auth.user.build_post(:photo, pending: true,
user_file: File.open(photo_fixture_name), to: "all").tap(&:save!)
}
@ -205,7 +181,7 @@ describe Api::V1::PostsController do
context "when given read-write access token" do
it "creates a public post" do
post_for_ref_only = auth_with_read_and_write.user.post(
post_for_ref_only = auth.user.post(
:status_message,
text: "Hello this is a public post!",
public: true,
@ -215,36 +191,51 @@ describe Api::V1::PostsController do
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
access_token: access_token,
body: "Hello this is a public post!",
public: true
}
)
expect(response.status).to eq(200)
post = response_body(response)
confirm_post_format(post, auth_with_read_and_write.user, post_for_ref_only)
confirm_post_format(post, auth.user, post_for_ref_only)
end
it "or creates a private post" do
aspect = Aspect.find_by(user_id: auth_with_read_and_write.user.id)
post_for_ref_only = auth_with_read_and_write.user.post(
aspect = auth.user.aspects.create(name: "new aspect")
post_for_ref_only = auth.user.post(
:status_message,
text: "Hello this is a private post!",
aspect_ids: [aspect[:id]]
aspect_ids: [aspect.id]
)
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
access_token: access_token,
body: "Hello this is a private post!",
public: false,
aspects: [aspect[:id]]
aspects: [aspect.id]
}
)
post = response_body(response)
expect(response.status).to eq(200)
confirm_post_format(post, auth_with_read_and_write.user, post_for_ref_only)
confirm_post_format(post, auth.user, post_for_ref_only)
end
it "doesn't creates a private post without private:modify scope in token" do
aspect = auth.user.aspects.create(name: "new aspect")
post(
api_v1_posts_path,
params: {
access_token: access_token_public_only,
body: "Hello this is a private post!",
public: false,
aspects: [aspect.id]
}
)
expect(response.status).to eq(422)
end
end
@ -253,12 +244,12 @@ describe Api::V1::PostsController do
message_text = "Post with photos"
base_params = {status_message: {text: message_text}, public: true}
merged_params = base_params.merge(photos: user_photo_ids)
post_for_ref_only = StatusMessageCreationService.new(auth_with_read_and_write.user).create(merged_params)
post_for_ref_only = StatusMessageCreationService.new(auth.user).create(merged_params)
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
access_token: access_token,
body: message_text,
public: true,
photos: user_photo_guids
@ -266,7 +257,7 @@ describe Api::V1::PostsController do
)
expect(response.status).to eq(200)
post = response_body(response)
confirm_post_format(post, auth_with_read_and_write.user, post_for_ref_only)
confirm_post_format(post, auth.user, post_for_ref_only)
end
it "fails to add other's photos" do
@ -275,7 +266,7 @@ describe Api::V1::PostsController do
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
access_token: access_token,
body: message_text,
public: true,
photos: alice_photo_guids
@ -292,7 +283,7 @@ describe Api::V1::PostsController do
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
access_token: access_token,
body: message_text,
public: true,
photos: ["999_999_999"]
@ -307,12 +298,12 @@ describe Api::V1::PostsController do
poll_params = {poll_question: "something?", poll_answers: %w[yes no maybe]}
base_params = {status_message: {text: message_text}, public: true}
merged_params = base_params.merge(poll_params)
post_for_ref_only = StatusMessageCreationService.new(auth_with_read_and_write.user).create(merged_params)
post_for_ref_only = StatusMessageCreationService.new(auth.user).create(merged_params)
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
access_token: access_token,
body: message_text,
public: true,
poll: {
@ -323,7 +314,7 @@ describe Api::V1::PostsController do
)
post = response_body(response)
expect(response.status).to eq(200)
confirm_post_format(post, auth_with_read_and_write.user, post_for_ref_only)
confirm_post_format(post, auth.user, post_for_ref_only)
end
it "fails poll with no answers" do
@ -331,7 +322,7 @@ describe Api::V1::PostsController do
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
access_token: access_token,
body: message_text,
public: true,
poll: {
@ -349,7 +340,7 @@ describe Api::V1::PostsController do
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
access_token: access_token,
body: message_text,
public: true,
poll: {
@ -366,7 +357,7 @@ describe Api::V1::PostsController do
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
access_token: access_token,
body: "",
public: true,
poll: {
@ -384,12 +375,12 @@ describe Api::V1::PostsController do
base_params = {status_message: {text: message_text}, public: true}
location_params = {location_address: "somewhere", location_coords: "1,2"}
merged_params = base_params.merge(location_params)
post_for_ref_only = StatusMessageCreationService.new(auth_with_read_and_write.user).create(merged_params)
post_for_ref_only = StatusMessageCreationService.new(auth.user).create(merged_params)
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
access_token: access_token,
body: message_text,
public: true,
location: {
@ -401,12 +392,12 @@ describe Api::V1::PostsController do
)
post = response_body(response)
expect(response.status).to eq(200)
confirm_post_format(post, auth_with_read_and_write.user, post_for_ref_only)
confirm_post_format(post, auth.user, post_for_ref_only)
end
it "creates with mentions" do
message_text = "hello @{#{alice.diaspora_handle}} from Bob!"
post_for_ref_only = auth_with_read_and_write.user.post(
post_for_ref_only = auth.user.post(
:status_message,
text: message_text,
public: true
@ -415,14 +406,14 @@ describe Api::V1::PostsController do
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
access_token: access_token,
body: message_text,
public: true
}
)
post = response_body(response)
expect(response.status).to eq(200)
confirm_post_format(post, auth_with_read_and_write.user, post_for_ref_only, [alice])
confirm_post_format(post, auth.user, post_for_ref_only, [alice])
end
end
@ -432,7 +423,7 @@ describe Api::V1::PostsController do
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
access_token: access_token,
body: message_text,
public: true
}
@ -459,7 +450,7 @@ describe Api::V1::PostsController do
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
access_token: access_token,
public: true
}
)
@ -472,7 +463,7 @@ describe Api::V1::PostsController do
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
access_token: access_token,
body: message_text
}
)
@ -485,7 +476,7 @@ describe Api::V1::PostsController do
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
access_token: access_token,
body: message_text,
public: false
}
@ -499,7 +490,7 @@ describe Api::V1::PostsController do
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
access_token: access_token,
body: message_text,
public: false,
aspects: ["-1"]
@ -510,14 +501,15 @@ describe Api::V1::PostsController do
end
it "fails when no public field but aspects" do
aspect = Aspect.find_by(user_id: auth_with_read_and_write.user.id)
aspect = auth.user.aspects.create(name: "new aspect")
auth.user.share_with(alice.person, aspect)
message_text = "hello @{#{alice.diaspora_handle}} from Bob!"
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
access_token: access_token,
body: message_text,
aspects: [aspect[:id]]
aspects: [aspect.id]
}
)
expect(response.status).to eq(422)
@ -530,7 +522,7 @@ describe Api::V1::PostsController do
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read,
access_token: access_token_read_only,
status_message: {text: "Hello this is a post!"},
public: true
}
@ -543,15 +535,15 @@ describe Api::V1::PostsController do
describe "#destroy" do
context "when given read-write access token" do
it "attempts to destroy the post" do
@status = auth_with_read_and_write.user.post(
@status = auth.user.post(
:status_message,
text: "hello",
public: true,
to: "all"
)
delete(
api_v1_post_path(@status.id),
params: {access_token: access_token_with_read_and_write}
api_v1_post_path(@status.guid),
params: {access_token: access_token}
)
expect(response.status).to eq(204)
end
@ -559,14 +551,31 @@ describe Api::V1::PostsController do
context "when given read only access token" do
it "doesn't delete the post" do
@status = auth_with_read.user.post(
@status = auth.user.post(
:status_message,
text: "hello",
public: true
)
delete(
api_v1_post_path(@status.id),
params: {access_token: access_token_with_read}
api_v1_post_path(@status.guid),
params: {access_token: access_token_read_only}
)
expect(response.status).to eq(403)
end
end
context "when post is private but no private:modify scope in token" do
it "doesn't delete the post" do
aspect = auth_public_only.user.aspects.create(name: "new aspect")
@status = auth_public_only.user.post(
:status_message,
text: "hello",
aspects: [aspect.id]
)
delete(
api_v1_post_path(@status.guid),
params: {access_token: access_token_public_only}
)
expect(response.status).to eq(403)
@ -577,7 +586,7 @@ describe Api::V1::PostsController do
it "doesn't delete a post" do
delete(
api_v1_post_path("999_999_999"),
params: {access_token: access_token_with_read_and_write}
params: {access_token: access_token}
)
expect(response.status).to eq(404)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.post_not_found"))
@ -595,7 +604,7 @@ describe Api::V1::PostsController do
delete(
api_v1_post_path(status.guid),
params: {access_token: access_token_with_read_and_write}
params: {access_token: access_token}
)
expect(response.status).to eq(403)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.failed_delete"))

View file

@ -3,10 +3,21 @@
require "spec_helper"
describe Api::V1::ResharesController do
let(:auth) { FactoryGirl.create(:auth_with_read_and_write) }
let(:auth) {
FactoryGirl.create(:auth_with_all_scopes)
}
let(:auth_read_only) {
FactoryGirl.create(:auth_with_read_scopes)
}
let(:auth_profile_only) {
FactoryGirl.create(:auth_with_profile_only)
}
let!(:access_token) { auth.create_access_token.to_s }
let(:auth_read_only) { FactoryGirl.create(:auth_with_read) }
let!(:access_token_read_only) { auth_read_only.create_access_token.to_s }
let!(:access_token_profile_only) { auth_profile_only.create_access_token.to_s }
before do
@user_post = auth.user.post(

View file

@ -3,10 +3,30 @@
require "spec_helper"
describe Api::V1::SearchController do
let(:auth) { FactoryGirl.create(:auth_with_read_and_write) }
let(:auth) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read public:modify private:read private:modify]
)
}
let(:auth_read_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read private:read]
)
}
let(:auth_public_only_read_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read]
)
}
let!(:access_token) { auth.create_access_token.to_s }
let(:auth_read_only) { FactoryGirl.create(:auth_with_read) }
let!(:access_token_read_only) { auth_read_only.create_access_token.to_s }
let!(:access_token_public_only_read_only) { auth_public_only_read_only.create_access_token.to_s }
describe "#user_index" do
before do
@ -40,7 +60,7 @@ describe Api::V1::SearchController do
)
expect(response.status).to eq(200)
users = response_body_data(response)
expect(users.length).to eq(14)
expect(users.length).to eq(15)
end
it "succeeds by name" do
@ -83,6 +103,19 @@ describe Api::V1::SearchController do
expect(users.length).to eq(0)
end
it "doesn't return hidden accounts who are linked without contacts:read token" do
aspect_to = auth_public_only_read_only.user.aspects.create(name: "shared aspect")
auth_public_only_read_only.user.share_with(@unsearchable_user, aspect_to)
get(
"/api/v1/search/users",
params: {name_or_handle: "unsearchable@example.org", access_token: access_token_public_only_read_only}
)
expect(response.status).to eq(200)
users = response_body_data(response)
expect(users.length).to eq(0)
end
it "fails if ask for both" do
get(
"/api/v1/search/users",
@ -123,16 +156,44 @@ describe Api::V1::SearchController do
text: "This is Eve's status message #tag2 #tag3",
public: true
)
aspect = eve.aspects.create(name: "shared aspect")
eve.share_with(auth_public_only_read_only.user.person, aspect)
eve.share_with(auth.user.person, aspect)
@eve_private_post = eve.post(
:status_message,
text: "This is Eve's status message #tag2 #tag3",
public: false,
to: aspect.id
)
end
it "succeeds by tag" do
get(
"/api/v1/search/posts",
params: {tag: "tag2", access_token: access_token_public_only_read_only}
)
expect(response.status).to eq(200)
posts = response_body_data(response)
expect(posts.length).to eq(2)
end
it "only returns public posts without private scope" do
get(
"/api/v1/search/posts",
params: {tag: "tag2", access_token: access_token_public_only_read_only}
)
expect(response.status).to eq(200)
posts = response_body_data(response)
expect(posts.length).to eq(2)
get(
"/api/v1/search/posts",
params: {tag: "tag2", access_token: access_token}
)
expect(response.status).to eq(200)
posts = response_body_data(response)
expect(posts.length).to eq(2)
expect(posts.length).to eq(3)
end
it "fails with missing parameters" do

View file

@ -3,57 +3,158 @@
require "spec_helper"
describe Api::V1::StreamsController do
let(:auth) { FactoryGirl.create(:auth_with_read_and_write) }
let!(:access_token) { auth.create_access_token.to_s }
let(:auth_read_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read private:read contacts:read tags:read]
)
}
let(:auth_public_only_tags) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read tags:read]
)
}
let(:auth_public_only_read_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read]
)
}
let!(:access_token_read_only) { auth_read_only.create_access_token.to_s }
let!(:access_token_public_only_tags) { auth_public_only_tags.create_access_token.to_s }
let!(:access_token_public_only_read_only) { auth_public_only_read_only.create_access_token.to_s }
before do
@aspect = auth.user.aspects.first
@created_status = auth.user.post(:status_message, text: "This is a status message #test", public: true, to: "all")
auth.user.like!(@created_status)
@status = PostService.new(auth.user).find(@created_status.id)
@aspect = auth_read_only.user.aspects.create(name: "new aspect")
auth_read_only.user.share_with(auth_public_only_read_only.user.person, @aspect)
@created_status = auth_read_only.user.post(
:status_message,
text: "This is a status message #test @{#{auth_read_only.user.diaspora_handle}}",
public: true
)
comment_service(auth_read_only.user).create(@created_status.guid, "Comment")
auth_read_only.user.like!(@created_status)
@status = PostService.new(auth_read_only.user).find(@created_status.id)
@private_post = auth_read_only.user.post(
:status_message,
text: "This is a private status message #test @{#{auth_read_only.user.diaspora_handle}}",
public: false,
to: @aspect.id
)
comment_service(auth_read_only.user).create(@private_post.guid, "Comment")
auth_read_only.user.like!(@private_post)
@created_status2 = auth_public_only_read_only.user.post(
:status_message,
text: "This is a status message #test @{#{auth_public_only_read_only.user.diaspora_handle}}",
public: true
)
auth_public_only_read_only.user.like!(@created_status2)
comment_service(auth_public_only_read_only.user).create(@created_status2.guid, "Comment")
@created_status3 = auth_public_only_read_only.user.post(
:status_message,
text: "This is a status message #test @{#{auth_public_only_read_only.user.diaspora_handle}}",
public: false,
to: "all"
)
auth_public_only_read_only.user.like!(@created_status3)
comment_service(auth_public_only_read_only.user).create(@created_status3.guid, "Comment")
add_tag("test", auth_read_only.user)
add_tag("test", auth_public_only_tags.user)
end
describe "#aspect" do
it "contains expected aspect message" do
get(
api_v1_aspects_stream_path(aspect_ids: JSON.generate([@aspect.id])),
params: {access_token: access_token}
params: {access_token: access_token_read_only}
)
expect(response.status).to eq 200
post = response_body_data(response)
expect(post.length).to eq 1
confirm_post_format(post[0], auth.user, @status)
expect(post.length).to eq 3
json_post = post.select {|p| p["guid"] == @status.guid }.first
confirm_post_format(json_post, auth_read_only.user, @status)
end
it "all aspects expected aspect message" do
get(
api_v1_aspects_stream_path,
params: {access_token: access_token}
params: {access_token: access_token_read_only}
)
expect(response.status).to eq 200
post = response_body_data(response)
expect(post.length).to eq 1
confirm_post_format(post[0], auth.user, @status)
expect(post.length).to eq 3
json_post = post.select {|p| p["guid"] == @status.guid }.first
confirm_post_format(json_post, auth_read_only.user, @status)
end
it "does not save to requested aspects to session" do
get(
api_v1_aspects_stream_path(a_ids: [@aspect.id]),
params: {access_token: access_token}
params: {access_token: access_token_read_only}
)
expect(session[:a_ids]).to be_nil
end
it "fails without impromper credentials" do
get(
api_v1_aspects_stream_path(a_ids: [@aspect.id]),
params: {access_token: access_token_public_only_read_only}
)
expect(response.status).to eq(403)
end
it "fails with invalid credentials" do
get(
api_v1_aspects_stream_path(a_ids: [@aspect.id]),
params: {access_token: "999_999_999"}
)
expect(response.status).to eq(401)
end
end
describe "#tags" do
it "all tags expected aspect message" do
it "all tags expected" do
get(
api_v1_followed_tags_stream_path,
params: {access_token: access_token}
params: {access_token: access_token_read_only}
)
expect(response.status).to eq 200
expect(response.status).to eq(200)
post = response_body_data(response)
expect(post.length).to eq 0
expect(post.length).to eq(3)
end
it "public posts only tags expected" do
get(
api_v1_followed_tags_stream_path,
params: {access_token: access_token_public_only_tags}
)
expect(response.status).to eq(200)
post = response_body_data(response)
expect(post.length).to eq(2)
end
it "fails with improper credentials" do
get(
api_v1_followed_tags_stream_path,
params: {access_token: access_token_public_only_read_only}
)
expect(response.status).to eq(403)
end
it "fails with invalid credentials" do
get(
api_v1_followed_tags_stream_path,
params: {access_token: "999_999_999"}
)
expect(response.status).to eq(401)
end
end
@ -61,12 +162,31 @@ describe Api::V1::StreamsController do
it "contains activity message" do
get(
api_v1_activity_stream_path,
params: {access_token: access_token}
params: {access_token: access_token_read_only}
)
expect(response.status).to eq 200
post = response_body_data(response)
expect(post.length).to eq 1
confirm_post_format(post[0], auth.user, @status)
expect(post.length).to eq 2
json_post = post.select {|p| p["guid"] == @status.guid }.first
confirm_post_format(json_post, auth_read_only.user, @status)
end
it "public posts only expected" do
get(
api_v1_activity_stream_path,
params: {access_token: access_token_public_only_read_only}
)
expect(response.status).to eq(200)
post = response_body_data(response)
expect(post.length).to eq(1)
end
it "fails with invalid credentials" do
get(
api_v1_activity_stream_path,
params: {access_token: "999_999_999"}
)
expect(response.status).to eq(401)
end
end
@ -74,12 +194,31 @@ describe Api::V1::StreamsController do
it "contains main message" do
get(
api_v1_stream_path,
params: {access_token: access_token}
params: {access_token: access_token_read_only}
)
expect(response.status).to eq 200
post = response_body_data(response)
expect(post.length).to eq 3
json_post = post.select {|p| p["guid"] == @status.guid }.first
confirm_post_format(json_post, auth_read_only.user, @status)
end
it "public posts only expected" do
get(
api_v1_stream_path,
params: {access_token: access_token_public_only_read_only}
)
expect(response.status).to eq 200
post = response_body_data(response)
expect(post.length).to eq 1
confirm_post_format(post[0], auth.user, @status)
end
it "fails with invalid credentials" do
get(
api_v1_stream_path,
params: {access_token: "999_999_999"}
)
expect(response.status).to eq 401
end
end
@ -87,11 +226,29 @@ describe Api::V1::StreamsController do
it "contains commented message" do
get(
api_v1_commented_stream_path,
params: {access_token: access_token}
params: {access_token: access_token_read_only}
)
expect(response.status).to eq 200
post = response_body_data(response)
expect(post.length).to eq 0
expect(post.length).to eq 2
end
it "public posts only expected" do
get(
api_v1_commented_stream_path,
params: {access_token: access_token_public_only_read_only}
)
expect(response.status).to eq 200
post = response_body_data(response)
expect(post.length).to eq 1
end
it "fails with invalid credentials" do
get(
api_v1_commented_stream_path,
params: {access_token: "999_999_999"}
)
expect(response.status).to eq 401
end
end
@ -99,11 +256,29 @@ describe Api::V1::StreamsController do
it "contains mentions message" do
get(
api_v1_mentions_stream_path,
params: {access_token: access_token}
params: {access_token: access_token_read_only}
)
expect(response.status).to eq 200
post = response_body_data(response)
expect(post.length).to eq 0
expect(post.length).to eq 2
end
it "public posts only expected" do
get(
api_v1_mentions_stream_path,
params: {access_token: access_token_public_only_read_only}
)
expect(response.status).to eq 200
post = response_body_data(response)
expect(post.length).to eq 1
end
it "fails with invalid credentials" do
get(
api_v1_mentions_stream_path,
params: {access_token: "999_999_999"}
)
expect(response.status).to eq 401
end
end
@ -111,12 +286,31 @@ describe Api::V1::StreamsController do
it "contains liked message" do
get(
api_v1_liked_stream_path,
params: {access_token: access_token}
params: {access_token: access_token_read_only}
)
expect(response.status).to eq 200
post = response_body_data(response)
expect(post.length).to eq 2
json_post = post.select {|p| p["guid"] == @status.guid }.first
confirm_post_format(json_post, auth_read_only.user, @status)
end
it "public posts only expected" do
get(
api_v1_liked_stream_path,
params: {access_token: access_token_public_only_read_only}
)
expect(response.status).to eq 200
post = response_body_data(response)
expect(post.length).to eq 1
confirm_post_format(post[0], auth.user, @status)
end
it "fails with invalid credentials" do
get(
api_v1_liked_stream_path,
params: {access_token: "999_999_999"}
)
expect(response.status).to eq 401
end
end
@ -209,4 +403,15 @@ describe Api::V1::StreamsController do
def response_body_data(response)
JSON.parse(response.body)["data"]
end
def add_tag(name, user)
tag = ActsAsTaggableOn::Tag.find_or_create_by(name: name)
tag_following = user.tag_followings.new(tag_id: tag.id)
tag_following.save
tag_following
end
def comment_service(user)
CommentService.new(user)
end
end

View file

@ -3,12 +3,29 @@
require "spec_helper"
describe Api::V1::TagFollowingsController do
let(:auth) { FactoryGirl.create(:auth_with_read_and_write) }
let(:auth) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid tags:read tags:modify]
)
}
let(:auth_read_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid tags:read]
)
}
let(:auth_profile_only) { FactoryGirl.create(:auth_with_profile_only) }
let!(:access_token) { auth.create_access_token.to_s }
let!(:access_token_read_only) { auth_read_only.create_access_token.to_s }
let!(:access_token_profile_only) { auth_profile_only.create_access_token.to_s }
before do
@expected_tags = %w[tag1 tag2 tag3]
@expected_tags.each {|tag| add_tag(tag, auth.user) }
@expected_tags.each {|tag| add_tag(tag, auth_read_only.user) }
@initial_count = @expected_tags.length
end
@ -38,7 +55,7 @@ describe Api::V1::TagFollowingsController do
params: {access_token: access_token}
)
expect(response.status).to eq(400)
expect(response.status).to eq(422)
expect(response.body).to eq(I18n.t("api.endpoint_errors.tags.cant_process"))
end
end
@ -50,10 +67,28 @@ describe Api::V1::TagFollowingsController do
params: {name: "tag3", access_token: access_token}
)
expect(response.status).to eq(400)
expect(response.status).to eq(422)
expect(response.body).to eq(I18n.t("api.endpoint_errors.tags.cant_process"))
end
end
context "fails with credentials" do
it "insufficient scopes in token" do
post(
api_v1_tag_followings_path,
params: {name: "tag4", access_token: access_token_read_only}
)
expect(response.status).to eq(403)
end
it "invalid token" do
post(
api_v1_tag_followings_path,
params: {name: "tag4", access_token: "999_999_999"}
)
expect(response.status).to eq(401)
end
end
end
describe "#index" do
@ -61,7 +96,7 @@ describe Api::V1::TagFollowingsController do
it "succeeds" do
get(
api_v1_tag_followings_path,
params: {access_token: access_token}
params: {access_token: access_token_read_only}
)
expect(response.status).to eq(200)
items = JSON.parse(response.body)
@ -69,6 +104,24 @@ describe Api::V1::TagFollowingsController do
@expected_tags.each {|tag| expect(items.find(tag)).to be_truthy }
end
end
context "fails with credentials" do
it "insufficient scopes in token" do
get(
api_v1_tag_followings_path,
params: {access_token: access_token_profile_only}
)
expect(response.status).to eq(403)
end
it "invalid token" do
get(
api_v1_tag_followings_path,
params: {access_token: "999_999_999"}
)
expect(response.status).to eq(401)
end
end
end
describe "#delete" do
@ -107,6 +160,24 @@ describe Api::V1::TagFollowingsController do
expect(items.length).to eq(@initial_count)
end
end
context "fails with credentials" do
it "insufficient scopes in token" do
delete(
api_v1_tag_following_path("tag1"),
params: {access_token: access_token_read_only}
)
expect(response.status).to eq(403)
end
it "invalid token" do
delete(
api_v1_tag_following_path("tag1"),
params: {access_token: "999_999_999"}
)
expect(response.status).to eq(401)
end
end
end
private

View file

@ -1,7 +1,10 @@
# frozen_string_literal: true
describe Api::OpenidConnect::UserInfoController do
let!(:auth_with_read_and_ppid) { FactoryGirl.create(:auth_with_read_and_ppid) }
let!(:auth_with_read_and_ppid) {
FactoryGirl.create(:auth_with_profile_and_ppid)
}
let!(:access_token_with_read) { auth_with_read_and_ppid.create_access_token.to_s }
describe "#show" do

View file

@ -5,10 +5,46 @@ require "spec_helper"
describe Api::V1::UsersController do
include PeopleHelper
let(:auth) { FactoryGirl.create(:auth_with_read_and_write) }
let(:auth_read_only) { FactoryGirl.create(:auth_with_read) }
let(:full_scopes) {
%w[openid public:read public:modify private:read private:modify contacts:read profile profile:modify]
}
let(:auth) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: full_scopes
)
}
let(:auth_public_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read public:modify]
)
}
let(:auth_read_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read private:read contacts:read profile]
)
}
let(:auth_public_only_read_only) {
FactoryGirl.create(
:auth_with_profile_only,
scopes: %w[openid public:read]
)
}
let(:auth_profile_only) {
FactoryGirl.create(:auth_with_profile_only)
}
let!(:access_token) { auth.create_access_token.to_s }
let!(:access_token_public_only) { auth_public_only.create_access_token.to_s }
let!(:access_token_read_only) { auth_read_only.create_access_token.to_s }
let!(:access_token_public_only_read_only) { auth_public_only_read_only.create_access_token.to_s }
let!(:access_token_profile_only) { auth_profile_only.create_access_token.to_s }
describe "#show" do
context "Current User" do
@ -49,7 +85,8 @@ describe Api::V1::UsersController do
it "succeeds with in Aspect valid user" do
alice.profile[:public_details] = true
alice.profile.save
auth.user.share_with(alice.person, auth.user.aspects.first)
aspect = auth.user.aspects.create(name: "first")
auth.user.share_with(alice.person, aspect)
get(
"/api/v1/users/#{alice.guid}",
params: {access_token: access_token}
@ -86,12 +123,31 @@ describe Api::V1::UsersController do
it "fails if invalid token" do
get(
api_v1_user_path(alice.person.guid),
"/api/v1/users/#{alice.guid}",
params: {access_token: "999_999_999"}
)
expect(response.status).to eq(401)
end
it "fails for private profile if don't have contacts:read" do
unsearchable_user = FactoryGirl.create(
:person,
diaspora_handle: "unsearchable@example.org",
profile: FactoryGirl.build(
:profile,
first_name: "Unsearchable",
last_name: "Person",
searchable: false
)
)
get(
"/api/v1/users/#{unsearchable_user.guid}",
params: {access_token: access_token_profile_only}
)
expect(response.status).to eq(404)
end
it "fails with invalid user GUID" do
get(
"/api/v1/users/999_999_999",
@ -240,7 +296,8 @@ describe Api::V1::UsersController do
contacts = response_body_data(response)
expect(contacts.length).to eq(0)
auth.user.share_with(alice.person, auth.user.aspects.first)
aspect = auth.user.aspects.create(name: "first")
auth.user.share_with(alice.person, aspect)
get(
api_v1_user_contacts_path(auth.user.guid),
params: {access_token: access_token}
@ -269,6 +326,14 @@ describe Api::V1::UsersController do
expect(response.body).to eq(I18n.t("api.endpoint_errors.users.not_found"))
end
it "fails if insufficient scope token" do
get(
api_v1_user_contacts_path(alice.guid),
params: {access_token: access_token_public_only_read_only}
)
expect(response.status).to eq(403)
end
it "fails if invalid token" do
get(
api_v1_user_contacts_path(alice.guid),
@ -284,10 +349,10 @@ describe Api::V1::UsersController do
alice.share_with(eve.person, alice_private_spec)
alice.share_with(auth.user.person, alice.aspects.first)
auth.user.post(:photo, pending: false, user_file: File.open(photo_fixture_name), to: "all")
auth.user.post(:photo, pending: false, user_file: File.open(photo_fixture_name), to: "all")
@public_photo1 = alice.post(:photo, pending: false, user_file: File.open(photo_fixture_name), to: "all")
@public_photo2 = alice.post(:photo, pending: false, user_file: File.open(photo_fixture_name), to: "all")
auth.user.post(:photo, pending: false, user_file: File.open(photo_fixture_name), public: true)
auth.user.post(:photo, pending: false, user_file: File.open(photo_fixture_name), public: true)
@public_photo1 = alice.post(:photo, pending: false, user_file: File.open(photo_fixture_name), public: true)
@public_photo2 = alice.post(:photo, pending: false, user_file: File.open(photo_fixture_name), public: true)
@shared_photo1 = alice.post(:photo, pending: false, user_file: File.open(photo_fixture_name),
to: alice.aspects.first.id)
@private_photo1 = alice.post(:photo, pending: false, user_file: File.open(photo_fixture_name),
@ -309,6 +374,19 @@ describe Api::V1::UsersController do
confirm_photos(photos)
end
it "returns only public photos of other user without private:read scope in token" do
get(
api_v1_user_photos_path(alice.guid),
params: {access_token: access_token_public_only_read_only}
)
expect(response.status).to eq(200)
photos = response_body_data(response)
expect(photos.length).to eq(2)
guids = photos.map {|photo| photo["guid"] }
expect(guids).to include(@public_photo1.guid, @public_photo2.guid)
expect(guids).not_to include(@private_photo1.guid, @shared_photo1.guid)
end
it "returns logged in user's photos" do
get(
api_v1_user_photos_path(auth.user.guid),
@ -344,10 +422,11 @@ describe Api::V1::UsersController do
alice.share_with(eve.person, alice_private_spec)
alice.share_with(auth.user.person, alice.aspects.first)
auth.user.post(:status_message, text: "auth user message1", public: true, to: "all")
auth.user.post(:status_message, text: "auth user message2", public: true, to: "all")
@public_post1 = alice.post(:status_message, text: "alice public message1", public: true, to: "all")
@public_post2 = alice.post(:status_message, text: "alice public message2", public: true, to: "all")
auth.user.post(:status_message, text: "auth user message1", public: true)
auth.user.post(:status_message, text: "auth user message2", public: true)
auth.user.post(:status_message, text: "auth user message3", public: false, to: "all")
@public_post1 = alice.post(:status_message, text: "alice public message1", public: true)
@public_post2 = alice.post(:status_message, text: "alice public message2", public: true)
@shared_post1 = alice.post(:status_message, text: "alice limited to auth user message",
public: false, to: alice.aspects.first.id)
@private_post1 = alice.post(:status_message, text: "alice limited hidden from auth user message",
@ -377,6 +456,16 @@ describe Api::V1::UsersController do
)
expect(response.status).to eq(200)
posts = response_body_data(response)
expect(posts.length).to eq(3)
end
it "returns public posts only without private:read scope in token" do
get(
api_v1_user_posts_path(auth.user.guid),
params: {access_token: access_token_public_only_read_only}
)
expect(response.status).to eq(200)
posts = response_body_data(response)
expect(posts.length).to eq(2)
end
end

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
describe Api::OpenidConnect::ProtectedResourceEndpoint, type: :request do
let(:auth_with_read) { FactoryGirl.create(:auth_with_read) }
let(:auth_with_read) { FactoryGirl.create(:auth_with_read_scopes) }
let!(:access_token_with_read) { auth_with_read.create_access_token.to_s }
let!(:expired_access_token) do
access_token = auth_with_read.o_auth_access_tokens.create!

View file

@ -64,9 +64,11 @@ describe Api::OpenidConnect::TokenEndpoint, type: :request do
end
it "should not allow a nil code" do
post api_openid_connect_access_tokens_path, params: {grant_type: "authorization_code",
client_id: client.client_id, client_secret: client.client_secret,
redirect_uri: "http://localhost:3000/", code: nil}
post api_openid_connect_access_tokens_path, params: {grant_type: "authorization_code",
client_id: client.client_id,
client_secret: client.client_secret,
redirect_uri: "http://localhost:3000/",
code: nil}
expect(JSON.parse(response.body)["error"]).to eq("invalid_request")
end
end

View file

@ -2,7 +2,7 @@
describe Api::OpenidConnect::IdToken, type: :model do
describe "#to_jwt" do
let(:auth) { FactoryGirl.create(:auth_with_read) }
let(:auth) { FactoryGirl.create(:auth_with_profile_only) }
let(:id_token) { Api::OpenidConnect::IdToken.new(auth, "nonce") }
describe "decoded data" do

View file

@ -181,6 +181,13 @@ describe PostService do
PostService.new(alice).destroy(post.id)
end
it "won't delete private post if explicitly unallowed" do
expect {
PostService.new(alice).destroy(post.id, false)
}.to raise_error Diaspora::NonPublic
expect(StatusMessage.find_by(id: post.id)).not_to be_nil
end
it "will not let you destroy posts visible to you but that you do not own" do
expect {
PostService.new(bob).destroy(post.id)

View file

@ -176,7 +176,7 @@ shared_examples_for "it updates user references" do
end
it "updates authorization refrences" do
authorization = FactoryGirl.create(:auth_with_read, user: old_user)
authorization = FactoryGirl.create(:auth_with_read_scopes, user: old_user)
run_migration
expect(authorization.reload.user).to eq(new_user)
end