diff --git a/examples/demo/site/config/cms.ini b/examples/demo/site/config/cms.ini index c24c612..5d51f43 100644 --- a/examples/demo/site/config/cms.ini +++ b/examples/demo/site/config/cms.ini @@ -12,3 +12,14 @@ theme=bootstrap smtp=localhost:25 #sendmail=/usr/bin/sendmail #output=@stderr + +[modules] +*=off +auth=on +basic_auth=on +blog=on +debug=on +demo=on +node=on +oauth20=on +openid=on diff --git a/examples/demo/src/ewf_roc_server_execution.e b/examples/demo/src/ewf_roc_server_execution.e index 5b62f8e..99a168a 100644 --- a/examples/demo/src/ewf_roc_server_execution.e +++ b/examples/demo/src/ewf_roc_server_execution.e @@ -55,38 +55,32 @@ feature -- CMS setup local m: CMS_MODULE do + -- Auth create {CMS_AUTHENTICATION_MODULE} m.make - m.enable a_setup.register_module (m) create {CMS_BASIC_AUTH_MODULE} m.make - m.enable a_setup.register_module (m) create {CMS_OAUTH_20_MODULE} m.make - m.enable a_setup.register_module (m) create {CMS_OPENID_MODULE} m.make - m.enable a_setup.register_module (m) + -- Nodes create {CMS_NODE_MODULE} m.make (a_setup) - m.enable a_setup.register_module (m) create {CMS_BLOG_MODULE} m.make - m.enable a_setup.register_module (m) + -- Miscellanious create {CMS_DEBUG_MODULE} m.make - m.enable a_setup.register_module (m) create {CMS_DEMO_MODULE} m.make - m.enable a_setup.register_module (m) - end end diff --git a/modules/basic_auth/cms_basic_auth_module.e b/modules/basic_auth/cms_basic_auth_module.e index 005cc07..09fefa3 100644 --- a/modules/basic_auth/cms_basic_auth_module.e +++ b/modules/basic_auth/cms_basic_auth_module.e @@ -37,7 +37,8 @@ feature {NONE} -- Initialization do version := "1.0" description := "Service to manage basic authentication" - package := "core" + package := "authentication" + add_dependency ({CMS_AUTHENTICATION_MODULE}) end feature -- Access diff --git a/modules/blog/cms_blog_module.e b/modules/blog/cms_blog_module.e index 77e49ef..c1bd05a 100644 --- a/modules/blog/cms_blog_module.e +++ b/modules/blog/cms_blog_module.e @@ -32,6 +32,7 @@ feature {NONE} -- Initialization version := "1.0" description := "Service to demonstrate new node for blog" package := "demo" + add_dependency ({CMS_NODE_MODULE}) end feature -- Access diff --git a/modules/oauth20/cms_oauth_20_module.e b/modules/oauth20/cms_oauth_20_module.e index b47cb62..5611059 100644 --- a/modules/oauth20/cms_oauth_20_module.e +++ b/modules/oauth20/cms_oauth_20_module.e @@ -49,7 +49,9 @@ feature {NONE} -- Initialization do version := "1.0" description := "OAuth20 module" - package := "Oauth20" + package := "authentication" + + add_dependency ({CMS_AUTHENTICATION_MODULE}) create root_dir.make_current cache_duration := 0 diff --git a/modules/openid/cms_openid_module.e b/modules/openid/cms_openid_module.e index f39e5d5..dcc0aa1 100644 --- a/modules/openid/cms_openid_module.e +++ b/modules/openid/cms_openid_module.e @@ -51,7 +51,8 @@ feature {NONE} -- Initialization do version := "1.0" description := "Openid module" - package := "openid" + package := "authentication" + add_dependency ({CMS_AUTHENTICATION_MODULE}) create root_dir.make_current cache_duration := 0 diff --git a/src/configuration/cms_default_setup.e b/src/configuration/cms_default_setup.e index 28718a1..167ebba 100644 --- a/src/configuration/cms_default_setup.e +++ b/src/configuration/cms_default_setup.e @@ -103,25 +103,7 @@ feature {NONE} -- Initialization initialize_modules -- Intialize core modules. - local --- m: CMS_MODULE do - -- Core --- create {BASIC_AUTH_MODULE} m.make --- m.enable --- register_module (m) - --- create {USER_MODULE} m.make (Current) --- m.enable --- register_module (m) - --- create {ADMIN_MODULE} m.make (Current) --- m.enable --- register_module (m) - --- create {NODE_MODULE} m.make (Current) --- m.enable --- register_module (m) end feature {NONE} -- Configuration diff --git a/src/configuration/cms_setup.e b/src/configuration/cms_setup.e index 51e6cbd..210f63a 100644 --- a/src/configuration/cms_setup.e +++ b/src/configuration/cms_setup.e @@ -33,16 +33,74 @@ feature -- Access modules as ic loop l_module := ic.item + update_module_status_from_configuration (l_module) if l_module.is_enabled then Result.extend (l_module) end end + across + Result as ic + loop + l_module := ic.item + update_module_status_within (l_module, Result) + if not l_module.is_enabled then + Result.remove (l_module) + end + end ensure only_enabled_modules: across Result as ic all ic.item.is_enabled end end feature {CMS_MODULE, CMS_API} -- Restricted access + update_module_status_within (a_module: CMS_MODULE; a_collection: CMS_MODULE_COLLECTION) + -- Is `a_module' enabled, and also its dependencies within the collection `a_collection'? + require + a_module_is_enabled: a_module.is_enabled + do + if attached a_module.dependencies as deps then + across + deps as ic + until + not a_module.is_enabled + loop + if + attached a_collection.item (ic.item) as mod and then + mod.is_enabled + then + update_module_status_within (mod, a_collection) + else + --| dependency not found or disabled + a_module.disable + end + end + end + end + + update_module_status_from_configuration (m: CMS_MODULE) + -- Is module `m' enabled? + local + b: BOOLEAN + dft: BOOLEAN + do + -- By default enabled. + if false and attached text_item ("modules.*") as l_mod_status then + dft := l_mod_status.is_case_insensitive_equal_general ("on") + else + dft := True + end + if attached text_item ("modules." + m.name) as l_mod_status then + b := l_mod_status.is_case_insensitive_equal_general ("on") + else + b := dft + end + if b then + m.enable + else + m.disable + end + end + modules: CMS_MODULE_COLLECTION -- List of available modules. deferred diff --git a/src/persistence/sql/cms_storage_sql_builder.e b/src/persistence/sql/cms_storage_sql_builder.e index a5b0f8f..a7b155c 100644 --- a/src/persistence/sql/cms_storage_sql_builder.e +++ b/src/persistence/sql/cms_storage_sql_builder.e @@ -57,7 +57,6 @@ feature -- Initialization l_authenticated_role.add_permission ("delete own page") l_authenticated_role.add_permission ("trash own page") a_storage.save_user_role (l_authenticated_role) - end note diff --git a/src/persistence/user/cms_user_storage_sql_i.e b/src/persistence/user/cms_user_storage_sql_i.e index 601e2ab..671fc20 100644 --- a/src/persistence/user/cms_user_storage_sql_i.e +++ b/src/persistence/user/cms_user_storage_sql_i.e @@ -194,8 +194,13 @@ feature -- Change: user sql_change (sql_insert_user, l_parameters) if not error_handler.has_error then a_user.set_id (last_inserted_user_id) + update_user_roles (a_user) + end + if not error_handler.has_error then + sql_commit_transaction + else + sql_rollback_transaction end - sql_commit_transaction else -- set error error_handler.add_custom_error (-1, "bad request" , "Missing password or email") @@ -224,6 +229,8 @@ feature -- Change: user l_password_hash /= Void and l_password_salt /= Void and attached a_user.email as l_email then + sql_begin_transaction + write_information_log (generator + ".update_user") create l_parameters.make (6) l_parameters.put (a_user.id, "uid") @@ -235,12 +242,101 @@ feature -- Change: user l_parameters.put (a_user.status, "status") sql_change (sql_update_user, l_parameters) + if not error_handler.has_error then + update_user_roles (a_user) + end + if not error_handler.has_error then + sql_commit_transaction + else + sql_rollback_transaction + end else -- set error error_handler.add_custom_error (-1, "bad request" , "Missing password or email") end end + update_user_roles (a_user: CMS_USER) + -- Update roles of `a_user' + require + a_user.has_id + local + l_roles, l_existing_roles: detachable LIST [CMS_USER_ROLE] + l_has_role: BOOLEAN + do + l_roles := a_user.roles + if l_roles = Void then + create {ARRAYED_LIST [CMS_USER_ROLE]} l_roles.make (0) + end + + sql_begin_transaction + + l_existing_roles:= user_roles_for (a_user) + across + l_existing_roles as ic + until + error_handler.has_error + loop + from + l_has_role := False + l_roles.start + until + l_has_role + loop + if l_roles.item.id = ic.item.id then + l_has_role := True + l_roles.remove -- Already stored. + else + l_roles.forth + end + end + if l_has_role then + -- Existing role has to be removed! + unassign_role_from_user (ic.item, a_user) + end + end + across + l_roles as ic + until + error_handler.has_error + loop + -- New role. + assign_role_to_user (ic.item, a_user) + end + + if not error_handler.has_error then + sql_commit_transaction + else + sql_rollback_transaction + end + end + + assign_role_to_user (a_role: CMS_USER_ROLE; a_user: CMS_USER) + require + a_user.has_id + a_role.has_id + local + l_parameters: STRING_TABLE [detachable ANY] + do + create l_parameters.make (2) + l_parameters.put (a_user.id, "uid") + l_parameters.put (a_role.id, "rid") + sql_change (sql_insert_role_to_user, l_parameters) + end + + unassign_role_from_user (a_role: CMS_USER_ROLE; a_user: CMS_USER) + require + a_user.has_id + a_role.has_id + local + l_parameters: STRING_TABLE [detachable ANY] + do + create l_parameters.make (2) + l_parameters.put (a_user.id, "uid") + l_parameters.put (a_role.id, "rid") + sql_change (sql_delete_role_from_user, l_parameters) + end + feature -- Access: roles and permissions user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE @@ -710,9 +806,15 @@ feature {NONE} -- Sql Queries: USER ROLE sql_update_user_role : STRING = "UPDATE roles SET name=:name WHERE rid=:rid;" -- Update user role with id :rid. - select_user_roles_by_user_id: STRING = "SELECT rid, name FROM roles INNER JOIN users_roles ON users_roles.rid=roles.rid WHERE users_roles.uid=:uid;" + select_user_roles_by_user_id: STRING = "SELECT users_roles.rid, roles.name FROM roles INNER JOIN users_roles ON users_roles.rid=roles.rid WHERE users_roles.uid=:uid;" -- List of user roles for user id :uid. + sql_insert_role_to_user: STRING = "INSERT INTO users_roles (uid, rid) VALUES (:uid, :rid);" + + sql_delete_role_from_user: STRING = "DELETE FROM users_roles WHERE uid=:uid AND rid=:rid;" + + sql_select_roles_ids_for_user: STRING = "SELECT rid FROM users_roles WHERE uid=:uid;" + select_user_role_by_id: STRING = "SELECT rid, name FROM roles WHERE rid=:rid;" -- User role for role id :rid; @@ -753,4 +855,7 @@ feature {NONE} -- User Password Recovery Select_user_by_password_token: STRING = "SELECT u.* FROM users as u JOIN users_password_recovery as ua ON ua.uid = u.uid and ua.token = :token;" -- Retrieve user by password token if exist. +note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/src/service/cms_api.e b/src/service/cms_api.e index 7235519..46de650 100644 --- a/src/service/cms_api.e +++ b/src/service/cms_api.e @@ -200,29 +200,10 @@ feature -- Query: module module (a_type: TYPE [CMS_MODULE]): detachable CMS_MODULE -- Enabled module typed `a_type', if any. --| usage: if attached module ({FOO_MODULE}) as mod then ... - local - l_type: TYPE [detachable CMS_MODULE] do - across - setup.modules as ic - until - Result /= Void - loop - Result := ic.item - if not Result.is_enabled then - Result := Void - else - l_type := Result.generating_type - if a_type ~ l_type then - -- Found - elseif - attached a_type.attempt (Result) and then attached l_type.generating_type.attempt (a_type) - then - -- Found - else - Result := Void - end - end + Result := setup.modules.item (a_type) + if Result /= Void and then not Result.is_enabled then + Result := Void end ensure Result /= Void implies (Result.is_enabled) -- and a_type.is_conforming_to (Result.generating_type)) diff --git a/src/service/cms_module.e b/src/service/cms_module.e index 0c46a25..1593999 100644 --- a/src/service/cms_module.e +++ b/src/service/cms_module.e @@ -23,11 +23,15 @@ feature -- Access -- Description of the module. package: STRING - -- + -- Associated package. + -- Mostly to group modules by package/category. version: STRING -- Version od the module? + dependencies: detachable LIST [TYPE [CMS_MODULE]] + -- Optional dependencies. + feature {CMS_API} -- Module Initialization initialize (api: CMS_API) @@ -42,6 +46,18 @@ feature {CMS_API} -- Module Initialization is_initialized: is_initialized end + add_dependency (m: TYPE [CMS_MODULE]) + local + deps: like dependencies + do + deps := dependencies + if deps = Void then + create {ARRAYED_LIST [TYPE [CMS_MODULE]]} deps.make (1) + dependencies := deps + end + deps.force (m) + end + feature -- Status is_initialized: BOOLEAN diff --git a/src/service/cms_module_collection.e b/src/service/cms_module_collection.e index 42411f1..041a78b 100644 --- a/src/service/cms_module_collection.e +++ b/src/service/cms_module_collection.e @@ -21,6 +21,54 @@ feature {NONE} -- Initialization feature -- Access + item (a_type: TYPE [CMS_MODULE]): detachable CMS_MODULE + -- Module typed `a_type', if any. + --| usage: if attached {FOO_MODULE} item ({FOO_MODULE}) as mod then ... + local + l_type: TYPE [detachable CMS_MODULE] + do + across + modules as ic + until + Result /= Void + loop + Result := ic.item + l_type := Result.generating_type + if a_type ~ l_type then + -- Found + elseif + -- Hack: use conformance of type, and reverse conformance of type of type. + attached a_type.attempt (Result) and then attached l_type.generating_type.attempt (a_type) + then + -- Found + else + Result := Void + end + end + ensure + Result /= Void implies (Result.is_enabled) + end + + item_by_name (a_name: READABLE_STRING_GENERAL): detachable CMS_MODULE + -- (first) module named `a_name', if any. + --| usage: if attached {FOO_MODULE} item_by_name ("foo") as mod then ... + do + across + modules as ic + until + Result /= Void + loop + Result := ic.item + if not a_name.is_case_insensitive_equal (Result.name) then + Result := Void + end + end + ensure + Result /= Void implies a_name.is_case_insensitive_equal (Result.name) + end + +feature -- Access: iteration + new_cursor: INDEXABLE_ITERATION_CURSOR [CMS_MODULE] -- do @@ -77,4 +125,7 @@ feature {NONE} -- Implementation modules: ARRAYED_LIST [CMS_MODULE] -- List of available modules. +;note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end