Fix authenticated role permissions, now it also has all anonymous permissions.

Added permissions on basic auth, to have more control on who can authenticate with basic auth.
Use webapi version of basic auth filter.
For webapi, when authenticated /api/user/ is the same as /api/user/{uid} where uid is the id of current logged in user.
This commit is contained in:
Jocelyn Fiat
2017-09-21 12:49:17 +02:00
parent 9d7d43073d
commit bc561b1a48
10 changed files with 135 additions and 61 deletions

View File

@@ -16,7 +16,9 @@ inherit
redefine redefine
make, make,
filters, filters,
setup_hooks setup_hooks,
install,
permissions
end end
CMS_WITH_WEBAPI CMS_WITH_WEBAPI
@@ -35,6 +37,17 @@ feature {NONE} -- Initialization
description := "Service to manage basic authentication" description := "Service to manage basic authentication"
end end
feature {CMS_API} -- Module management
install (a_api: CMS_API)
do
Precursor (a_api)
if attached a_api.user_api.anonymous_user_role as ano then
ano.add_permission (perm_use_basic_auth)
a_api.user_api.save_user_role (ano)
end
end
feature {CMS_EXECUTION} -- Administration feature {CMS_EXECUTION} -- Administration
webapi: CMS_BASIC_AUTH_MODULE_WEBAPI webapi: CMS_BASIC_AUTH_MODULE_WEBAPI
@@ -46,6 +59,15 @@ feature -- Access
name: STRING = "basic_auth" name: STRING = "basic_auth"
permissions: LIST [READABLE_STRING_8]
-- List of permission ids, used by this module, and declared.
do
Result := Precursor
Result.force ("use basic_auth")
end
perm_use_basic_auth: STRING = "use basic_auth"
feature -- Access: auth strategy feature -- Access: auth strategy
login_title: STRING = "Basic Auth" login_title: STRING = "Basic Auth"

View File

@@ -28,7 +28,7 @@ feature -- Access: filter
-- Possibly list of Filter's module. -- Possibly list of Filter's module.
do do
create {ARRAYED_LIST [WSF_FILTER]} Result.make (1) create {ARRAYED_LIST [WSF_FILTER]} Result.make (1)
Result.extend (create {CMS_BASIC_AUTH_FILTER}.make (a_api)) Result.extend (create {CMS_BASIC_WEBAPI_AUTH_FILTER}.make (a_api))
end end
note note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"

View File

@@ -38,12 +38,14 @@ feature -- Basic operations
api.user_api.is_valid_credential (l_auth_login, l_auth_password) and then api.user_api.is_valid_credential (l_auth_login, l_auth_password) and then
attached api.user_api.user_by_name (l_auth_login) as l_user attached api.user_api.user_by_name (l_auth_login) as l_user
then then
debug ("refactor_fixme") if api.user_has_permission (l_user, {CMS_BASIC_AUTH_MODULE}.perm_use_basic_auth) then
fixme ("Maybe we need to store in the credentials in a shared context SECURITY_CONTEXT") debug ("refactor_fixme")
-- req.set_execution_variable ("security_content", create SECURITY_CONTEXT.make (l_user)) fixme ("Maybe we need to store in the credentials in a shared context SECURITY_CONTEXT")
-- other authentication filters (OpenID, etc) should implement the same approach. -- req.set_execution_variable ("security_content", create SECURITY_CONTEXT.make (l_user))
-- other authentication filters (OpenID, etc) should implement the same approach.
end
set_current_user (l_user)
end end
set_current_user (l_user)
else else
api.logger.put_error (generator + ".execute login_valid failed for: " + l_auth_login, Void) api.logger.put_error (generator + ".execute login_valid failed for: " + l_auth_login, Void)
end end

View File

@@ -29,7 +29,9 @@ feature -- Basic operations
api.user_api.is_valid_credential (l_auth_login, l_auth_password) and then api.user_api.is_valid_credential (l_auth_login, l_auth_password) and then
attached api.user_api.user_by_name (l_auth_login) as l_user attached api.user_api.user_by_name (l_auth_login) as l_user
then then
api.set_user (l_user) if api.user_has_permission (l_user, {CMS_BASIC_AUTH_MODULE}.perm_use_basic_auth) then
api.set_user (l_user)
end
else else
-- not authenticated due to bad login or password. -- not authenticated due to bad login or password.
end end

View File

@@ -247,15 +247,16 @@ feature -- Status report
do do
if a_permission = Void then if a_permission = Void then
Result := True Result := True
elseif a_user = Void then
Result := user_role_has_permission (anonymous_user_role, a_permission)
else else
if is_admin_user (a_user) then Result := user_role_has_permission (anonymous_user_role, a_permission)
Result := True if not Result and a_user /= Void then
else if is_admin_user (a_user) then
Result := user_role_has_permission (authenticated_user_role, a_permission) Result := True
if not Result then else
Result := across user_roles (a_user) as ic some user_role_has_permission (ic.item, a_permission) end Result := user_role_has_permission (authenticated_user_role, a_permission)
if not Result then
Result := across user_roles (a_user) as ic some user_role_has_permission (ic.item, a_permission) end
end
end end
end end
end end

View File

@@ -51,7 +51,7 @@ feature -- Request execution
do do
if attached user_by_uid (a_uid) as l_user then if attached user_by_uid (a_uid) as l_user then
if attached api.user as u then if attached api.user as u then
if u.same_as (l_user) or api.user_api.is_admin_user (u) then if u.same_as (l_user) and api.has_permission ("use access_token") then
rep := new_access_token_response (l_user, user_access_token (l_user), req, res) rep := new_access_token_response (l_user, user_access_token (l_user), req, res)
if attached {WSF_STRING} req.item ("destination") as dest then if attached {WSF_STRING} req.item ("destination") as dest then
rep.set_redirection (dest.url_encoded_value) rep.set_redirection (dest.url_encoded_value)
@@ -77,18 +77,13 @@ feature -- Request execution
do do
if attached user_by_uid (a_uid) as l_user then if attached user_by_uid (a_uid) as l_user then
if attached api.user as u then if attached api.user as u then
if u.same_as (l_user) or api.user_api.is_admin_user (u) then if
u.same_as (l_user) and api.has_permission ("use access_token")
then
if attached req.path_parameter ("application") then if attached req.path_parameter ("application") then
end end
-- l_access_token := user_access_token (l_user)
l_access_token := new_key (40) l_access_token := new_key (40)
-- if l_access_token /= Void then
-- l_access_token := "Updated-" + (create {UUID_GENERATOR}).generate_uuid.out
-- else
-- l_access_token := "New-" + (create {UUID_GENERATOR}).generate_uuid.out
-- end
set_user_access_token (l_user, l_access_token) set_user_access_token (l_user, l_access_token)
rep := new_access_token_response (l_user, l_access_token, req, res) rep := new_access_token_response (l_user, l_access_token, req, res)

View File

@@ -1,6 +1,5 @@
note note
description: "Summary description for {CMS_CORE_MODULE_WEBAPI}." description: "Summary description for {CMS_CORE_MODULE_WEBAPI}."
author: ""
date: "$Date$" date: "$Date$"
revision: "$Revision$" revision: "$Revision$"
@@ -25,6 +24,7 @@ feature -- Security
Result := Precursor Result := Precursor
Result.force ("admin users") Result.force ("admin users")
Result.force ("view users") Result.force ("view users")
Result.force ("use access_token")
end end
feature {NONE} -- Router/administration feature {NONE} -- Router/administration
@@ -35,11 +35,13 @@ feature {NONE} -- Router/administration
l_root: CMS_ROOT_WEBAPI_HANDLER l_root: CMS_ROOT_WEBAPI_HANDLER
do do
create l_root.make (a_api) create l_root.make (a_api)
l_root.set_router (a_router)
a_router.handle ("", l_root, a_router.methods_get) a_router.handle ("", l_root, a_router.methods_get)
a_router.handle ("/", l_root, a_router.methods_get) a_router.handle ("/", l_root, a_router.methods_get)
a_router.handle ("/user/{uid}/access_token", create {CMS_ACCESS_TOKEN_WEBAPI_HANDLER}.make (a_api), a_router.methods_get_post) a_router.handle ("/user/{uid}/access_token", create {CMS_ACCESS_TOKEN_WEBAPI_HANDLER}.make (a_api), a_router.methods_get_post)
a_router.handle ("/user/{uid}", create {CMS_USER_WEBAPI_HANDLER}.make (a_api), a_router.methods_get) a_router.handle ("/user/{uid}", create {CMS_USER_WEBAPI_HANDLER}.make (a_api), a_router.methods_get)
a_router.handle ("/user/", create {CMS_USERS_WEBAPI_HANDLER}.make (a_api), a_router.methods_get_post) a_router.handle ("/user/", create {CMS_USER_WEBAPI_HANDLER}.make (a_api), a_router.methods_get)
a_router.handle ("/users/", create {CMS_USERS_WEBAPI_HANDLER}.make (a_api), a_router.methods_get_post)
end end
feature -- Access: filter feature -- Access: filter
@@ -49,7 +51,6 @@ feature -- Access: filter
do do
create {ARRAYED_LIST [WSF_FILTER]} Result.make (2) create {ARRAYED_LIST [WSF_FILTER]} Result.make (2)
Result.extend (create {CMS_ACCESS_TOKEN_WEBAPI_AUTH_FILTER}.make (a_api)) Result.extend (create {CMS_ACCESS_TOKEN_WEBAPI_AUTH_FILTER}.make (a_api))
Result.extend (create {CMS_BASIC_WEBAPI_AUTH_FILTER}.make (a_api))
end end
note note

View File

@@ -14,12 +14,25 @@ inherit
create create
make make
feature -- Access
router: detachable WSF_ROUTER
feature -- Element change
set_router (a_router: like router)
do
router := a_router
end
feature -- Execution feature -- Execution
execute (req: WSF_REQUEST; res: WSF_RESPONSE) execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute handler for `req' and respond in `res'. -- Execute handler for `req' and respond in `res'.
local local
rep: HM_WEBAPI_RESPONSE rep: HM_WEBAPI_RESPONSE
vis: WSF_ROUTER_AGENT_ITERATOR
j: JSON_ARRAY
do do
rep := new_response (req, res) rep := new_response (req, res)
rep.add_string_field ("site_name", api.setup.site_name) rep.add_string_field ("site_name", api.setup.site_name)
@@ -28,6 +41,44 @@ feature -- Execution
elseif api.has_permission ("account register") then elseif api.has_permission ("account register") then
rep.add_link ("register", Void, api.webapi_path ("/account/register")) rep.add_link ("register", Void, api.webapi_path ("/account/register"))
end end
-- If query has "router=yes", display basic information about router mapping.
-- Note: this may change in the future
if
attached router as l_router and then
attached req.query_parameter ("router") as p_router and then
p_router.same_string ("yes")
then
create j.make_empty
create vis
vis.on_item_actions.extend (agent (i_item: WSF_ROUTER_ITEM; i_json: JSON_ARRAY)
local
jo: JSON_OBJECT
s: STRING
do
create jo.make_with_capacity (3)
jo.put_string (i_item.mapping.associated_resource, "resource")
create s.make_empty
if attached i_item.request_methods as methds and then not methds.is_empty then
across
methds as ic
loop
if not s.is_empty then
s.extend (',')
end
s.append (ic.item)
end
jo.put_string (s, "request_methods")
else
s.append ("*")
end
jo.put_string (i_item.mapping.description, "description")
i_json.extend (jo)
end(?, j))
vis.process_router (l_router)
rep.add_string_field ("routing", j.representation)
end
rep.add_self (req.percent_encoded_path_info) rep.add_self (req.percent_encoded_path_info)
rep.execute rep.execute
end end

View File

@@ -41,41 +41,41 @@ feature -- Execution
else else
l_user := api.user_api.user_by_name (p_uid.value) l_user := api.user_api.user_by_name (p_uid.value)
end end
-- if l_user = Void and p_uid.is_case_insensitive_equal ("me") then else
-- l_user := u -- if uid is not give, use current user if any
-- end l_user := u
if l_user /= Void then end
if l_user.same_as (u) or api.has_permissions (<<"admin users", "view users">>) then -- if l_user = Void and p_uid.is_case_insensitive_equal ("me") then
rep := new_response (req, res) -- l_user := u
rep.add_string_field ("uid", l_user.id.out) -- end
rep.add_string_field ("name", l_user.name) if l_user /= Void then
if attached l_user.email as l_email then if l_user.same_as (u) or api.has_permissions (<<"admin users", "view users">>) then
rep.add_string_field ("email", l_email) rep := new_response (req, res)
end rep.add_string_field ("uid", l_user.id.out)
if not l_user.is_active then rep.add_string_field ("name", l_user.name)
rep.add_boolean_field ("is_active", False) if attached l_user.email as l_email then
end rep.add_string_field ("email", l_email)
if attached l_user.profile_name as l_profile_name then
rep.add_string_field ("profile_name", l_profile_name)
end
if attached l_user.creation_date as dt then
rep.add_string_field ("creation_date", date_time_to_string (dt))
end
if attached l_user.last_login_date as dt then
rep.add_string_field ("last_login_date", date_time_to_string (dt))
end
add_user_links_to (l_user, rep)
else
rep := new_error_response ("denied", req, res)
rep.set_status_code ({HTTP_STATUS_CODE}.user_access_denied)
end end
if not l_user.is_active then
rep.add_boolean_field ("is_active", False)
end
if attached l_user.profile_name as l_profile_name then
rep.add_string_field ("profile_name", l_profile_name)
end
if attached l_user.creation_date as dt then
rep.add_string_field ("creation_date", date_time_to_string (dt))
end
if attached l_user.last_login_date as dt then
rep.add_string_field ("last_login_date", date_time_to_string (dt))
end
add_user_links_to (l_user, rep)
else else
rep := new_error_response ("Not found", req, res) rep := new_error_response ("denied", req, res)
rep.set_status_code ({HTTP_STATUS_CODE}.not_found) rep.set_status_code ({HTTP_STATUS_CODE}.user_access_denied)
end end
else else
rep := new_error_response ("Bad request", req, res) rep := new_error_response ("Not found", req, res)
rep.set_status_code ({HTTP_STATUS_CODE}.bad_request) rep.set_status_code ({HTTP_STATUS_CODE}.not_found)
end end
else else
-- FIXME: use specific Web API response! -- FIXME: use specific Web API response!

View File

@@ -51,13 +51,13 @@ feature {CMS_API, CMS_MODULE_ADMINISTRATION, CMS_MODULE_WEBAPI} -- Access: API
feature -- Status feature -- Status
is_initialized: BOOLEAN frozen is_initialized: BOOLEAN
-- Is Current module initialized? -- Is Current module initialized?
do do
Result := module.is_initialized Result := module.is_initialized
end end
is_enabled: BOOLEAN frozen is_enabled: BOOLEAN
-- Is Current module enabled? -- Is Current module enabled?
do do
Result := module.is_enabled Result := module.is_enabled