SECURITY: inactive/suspended accounts should be banned from api

Also fixes edge cases around users presenting multiple credentials
This commit is contained in:
Sam 2017-02-17 11:02:33 -05:00
parent c0e1722ca6
commit 7a85469c4c
2 changed files with 37 additions and 11 deletions

View File

@ -36,7 +36,10 @@ class Auth::DefaultCurrentUserProvider
request = @request request = @request
auth_token = request.cookies[TOKEN_COOKIE] user_api_key = @env[USER_API_KEY]
api_key = request[API_KEY]
auth_token = request.cookies[TOKEN_COOKIE] unless user_api_key || api_key
current_user = nil current_user = nil
@ -61,10 +64,6 @@ class Auth::DefaultCurrentUserProvider
end end
end end
if current_user && (current_user.suspended? || !current_user.active)
current_user = nil
end
if current_user && should_update_last_seen? if current_user && should_update_last_seen?
u = current_user u = current_user
Scheduler::Defer.later "Updating Last Seen" do Scheduler::Defer.later "Updating Last Seen" do
@ -74,17 +73,18 @@ class Auth::DefaultCurrentUserProvider
end end
# possible we have an api call, impersonate # possible we have an api call, impersonate
if api_key = request[API_KEY] if api_key
current_user = lookup_api_user(api_key, request) current_user = lookup_api_user(api_key, request)
raise Discourse::InvalidAccess unless current_user raise Discourse::InvalidAccess unless current_user
raise Discourse::InvalidAccess if current_user.suspended? || !current_user.active
@env[API_KEY_ENV] = true @env[API_KEY_ENV] = true
end end
# user api key handling # user api key handling
if api_key = @env[USER_API_KEY] if user_api_key
limiter_min = RateLimiter.new(nil, "user_api_min_#{api_key}", SiteSetting.max_user_api_reqs_per_minute, 60) limiter_min = RateLimiter.new(nil, "user_api_min_#{user_api_key}", SiteSetting.max_user_api_reqs_per_minute, 60)
limiter_day = RateLimiter.new(nil, "user_api_day_#{api_key}", SiteSetting.max_user_api_reqs_per_day, 86400) limiter_day = RateLimiter.new(nil, "user_api_day_#{user_api_key}", SiteSetting.max_user_api_reqs_per_day, 86400)
unless limiter_day.can_perform? unless limiter_day.can_perform?
limiter_day.performed! limiter_day.performed!
@ -94,8 +94,9 @@ class Auth::DefaultCurrentUserProvider
limiter_min.performed! limiter_min.performed!
end end
current_user = lookup_user_api_user_and_update_key(api_key, @env[USER_API_CLIENT_ID]) current_user = lookup_user_api_user_and_update_key(user_api_key, @env[USER_API_CLIENT_ID])
raise Discourse::InvalidAccess unless current_user raise Discourse::InvalidAccess unless current_user
raise Discourse::InvalidAccess if current_user.suspended? || !current_user.active
limiter_min.performed! limiter_min.performed!
limiter_day.performed! limiter_day.performed!
@ -103,6 +104,12 @@ class Auth::DefaultCurrentUserProvider
@env[USER_API_KEY_ENV] = true @env[USER_API_KEY_ENV] = true
end end
# keep this rule here as a safeguard
# under no conditions to suspended or inactive accounts get current_user
if current_user && (current_user.suspended? || !current_user.active)
current_user = nil
end
@env[CURRENT_USER_KEY] = current_user @env[CURRENT_USER_KEY] = current_user
end end
@ -112,7 +119,7 @@ class Auth::DefaultCurrentUserProvider
# it could be an anonymous path, this would add cost # it could be an anonymous path, this would add cost
return if is_api? || !@env.key?(CURRENT_USER_KEY) return if is_api? || !@env.key?(CURRENT_USER_KEY)
if @user_token && @user_token.user == user if !is_user_api? && @user_token && @user_token.user == user
rotated_at = @user_token.rotated_at rotated_at = @user_token.rotated_at
needs_rotation = @user_token.auth_token_seen ? rotated_at < UserAuthToken::ROTATE_TIME.ago : rotated_at < UserAuthToken::URGENT_ROTATE_TIME.ago needs_rotation = @user_token.auth_token_seen ? rotated_at < UserAuthToken::ROTATE_TIME.ago : rotated_at < UserAuthToken::URGENT_ROTATE_TIME.ago

View File

@ -26,6 +26,18 @@ describe Auth::DefaultCurrentUserProvider do
user = Fabricate(:user) user = Fabricate(:user)
ApiKey.create!(key: "hello", user_id: user.id, created_by_id: -1) ApiKey.create!(key: "hello", user_id: user.id, created_by_id: -1)
expect(provider("/?api_key=hello").current_user.id).to eq(user.id) expect(provider("/?api_key=hello").current_user.id).to eq(user.id)
user.update_columns(active: false)
expect{
provider("/?api_key=hello").current_user
}.to raise_error(Discourse::InvalidAccess)
user.update_columns(active: true, suspended_till: 1.day.from_now)
expect{
provider("/?api_key=hello").current_user
}.to raise_error(Discourse::InvalidAccess)
end end
it "raises for a user pretending" do it "raises for a user pretending" do
@ -248,6 +260,13 @@ describe Auth::DefaultCurrentUserProvider do
provider("/", params.merge({"REQUEST_METHOD" => "POST"})).current_user provider("/", params.merge({"REQUEST_METHOD" => "POST"})).current_user
}.to raise_error(Discourse::InvalidAccess) }.to raise_error(Discourse::InvalidAccess)
user.update_columns(suspended_till: 1.year.from_now)
expect {
provider("/", params).current_user
}.to raise_error(Discourse::InvalidAccess)
end end
it "rate limits api usage" do it "rate limits api usage" do