Revisited the persistence layer.

Simplified schema to focus on user and node.
Now possible to have sqlite via ODBC and/or mysql support, and select using configuration file.
Updated demo example.
This commit is contained in:
2015-01-27 19:48:37 +01:00
parent db9e40cec4
commit 7d5869f3b9
71 changed files with 2665 additions and 3313 deletions

View File

@@ -1,166 +0,0 @@
note
description: "Summary description for {CMS_STORAGE_NULL}."
date: "$Date$"
revision: "$Revision$"
class
CMS_STORAGE_NULL
inherit
CMS_STORAGE
redefine
default_create
select
default_create
end
REFACTORING_HELPER
rename
default_create as default_create_rh
end
feature -- Initialization
default_create
do
create error_handler.make
end
feature -- Access: user
has_user: BOOLEAN
-- Has any user?
do
end
all_users: LIST [CMS_USER]
do
create {ARRAYED_LIST[CMS_USER]} Result.make (0)
end
user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER
do
end
user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER
do
end
user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER
do
end
is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN
do
end
feature -- User Nodes
user_collaborator_nodes (a_id: like {CMS_USER}.id): LIST[CMS_NODE]
-- Possible list of nodes where the user identified by `a_id', is a collaborator.
do
create {ARRAYED_LIST[CMS_NODE]} Result.make (0)
end
user_author_nodes (a_id: like {CMS_USER}.id): LIST[CMS_NODE]
-- Possible list of nodes where the user identified by `a_id', is the author.
do
create {ARRAYED_LIST[CMS_NODE]} Result.make (0)
end
feature -- Change: user
save_user (a_user: CMS_USER)
-- Add a new user `a_user'.
do
end
feature -- Access: roles and permissions
user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE
do
end
user_roles: LIST [CMS_USER_ROLE]
do
create {ARRAYED_LIST[CMS_USER_ROLE]} Result.make (0)
end
feature -- Change: roles and permissions
save_user_role (a_user_role: CMS_USER_ROLE)
do
end
feature -- Access: node
nodes: LIST[CMS_NODE]
-- List of nodes.
do
create {ARRAYED_LIST[CMS_NODE]} Result.make (0)
end
recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE]
-- List of the `a_count' most recent nodes, starting from `a_lower'.
do
create {ARRAYED_LIST[CMS_NODE]} Result.make (0)
end
node (a_id: INTEGER_64): detachable CMS_NODE
-- <Precursor>
do
end
node_author (a_id: like {CMS_NODE}.id): detachable CMS_USER
-- Node's author. if any.
do
end
node_collaborators (a_id: like {CMS_NODE}.id): LIST [CMS_USER]
-- Possible list of node's collaborator.
do
create {ARRAYED_LIST[CMS_USER]} Result.make (0)
end
feature -- Node
save_node (a_node: CMS_NODE)
-- Add a new node
do
end
delete_node (a_id: INTEGER_64)
-- <Precursor>
do
end
update_node (a_id: like {CMS_NODE}.id; a_node: CMS_NODE)
-- <Precursor>
do
end
update_node_title (a_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32)
-- <Precursor>
do
end
update_node_summary (a_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32)
-- <Precursor>
do
end
update_node_content (a_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32)
-- <Precursor>
do
end
feature -- User
new_user (a_user: CMS_USER)
-- Add a new user `a_user'.
do
end
end

View File

@@ -0,0 +1,111 @@
note
description: "Summary description for {CMS_STORAGE_STORE_SQL}."
author: ""
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
deferred class
CMS_STORAGE_STORE_SQL
inherit
CMS_STORAGE
CMS_STORAGE_SQL
feature {NONE} -- Initialization
make (a_connection: DATABASE_CONNECTION)
--
require
is_connected: a_connection.is_connected
do
connection := a_connection
log.write_information (generator + ".make - is database connected? "+ a_connection.is_connected.out )
create {DATABASE_HANDLER_IMPL} db_handler.make (a_connection)
create error_handler.make
-- error_handler.add_synchronization (db_handler.database_error_handler)
end
feature {NONE} -- Implementation
db_handler: DATABASE_HANDLER
connection: DATABASE_CONNECTION
-- Current database connection.
feature -- Query
sql_post_execution
-- Post database execution.
do
error_handler.append (db_handler.database_error_handler)
if error_handler.has_error then
log.write_critical (generator + ".post_execution " + error_handler.as_string_representation)
end
end
sql_begin_transaction
do
connection.begin_transaction
end
sql_rollback_transaction
do
connection.rollback
end
sql_commit_transaction
do
connection.commit
end
sql_query (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY])
do
check_sql_query_validity (a_sql_statement, a_params)
db_handler.set_query (create {DATABASE_QUERY}.data_reader (a_sql_statement, a_params))
db_handler.execute_query
end
sql_change (a_sql_statement: STRING; a_params: detachable STRING_TABLE [detachable ANY])
do
check_sql_query_validity (a_sql_statement, a_params)
db_handler.set_query (create {DATABASE_QUERY}.data_reader (a_sql_statement, a_params))
db_handler.execute_change
end
sql_rows_count: INTEGER
-- Number of rows for last sql execution.
do
Result := db_handler.count
end
sql_start
-- Set the cursor on first element.
do
db_handler.start
end
sql_after: BOOLEAN
-- Are there no more items to iterate over?
do
Result := db_handler.after
end
sql_forth
-- Fetch next row from last sql execution, if any.
do
db_handler.forth
end
sql_item (a_index: INTEGER): detachable ANY
do
if attached {DB_TUPLE} db_handler.item as l_item and then l_item.count >= a_index then
Result := l_item.item (a_index)
else
check has_item_at_index: False end
end
end
end

View File

@@ -1,7 +1,7 @@
note
description: "Abstract Database Handler"
date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $"
revision: "$Revision: 95678 $"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
deferred class
DATABASE_HANDLER
@@ -45,14 +45,14 @@ feature -- Functionality Store Procedures
execute_store_reader
-- Execute a `store' to read data.
require
store_not_void: store /= void
store_not_void: store /= Void
deferred
end
execute_store_writer
-- Execute a `store' to write data.
require
store_not_void: store /= void
store_not_void: store /= Void
deferred
end
@@ -61,14 +61,14 @@ feature -- SQL Queries
execute_query
-- Execute sql query, the read data from the database.
require
query_not_void: query /= void
query_not_void: query /= Void
deferred
end
execute_change
-- Execute sql query that update/add data.
require
query_not_void: query /= void
query_not_void: query /= Void
deferred
end
@@ -153,22 +153,22 @@ feature -- Status Report
end
connection: DATABASE_CONNECTION
-- Database connection.
-- Database connection.
db_control: DB_CONTROL
-- Database control.
-- Database control.
do
Result := connection.db_control
end
db_result: detachable DB_RESULT
-- Database query result.
-- Database query result.
db_selection: detachable DB_SELECTION
-- Database selection.
-- Database selection.
db_change: detachable DB_CHANGE
-- Database modification.
-- Database modification.
feature -- Error handling

View File

@@ -1,13 +1,12 @@
note
description: "Abstract Database Query"
date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $"
revision: "$Revision: 95678 $"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
class
DATABASE_QUERY
inherit
REFACTORING_HELPER
SHARED_LOGGER
@@ -17,7 +16,7 @@ create
feature {NONE} -- Intialization
data_reader (a_query: STRING; a_parameters: STRING_TABLE [detachable ANY])
data_reader (a_query: STRING; a_parameters: like parameters)
-- SQL data reader for the query `a_query' with arguments `a_parameters'
do
log.write_information (generator + ".data_reader" + " execute query: " + a_query)
@@ -65,7 +64,7 @@ feature -- Access
query: STRING
-- SQL query to execute.
parameters: STRING_TABLE [detachable ANY]
parameters: detachable STRING_TABLE [detachable ANY]
-- query parameters.
feature {NONE} -- Implementation
@@ -73,26 +72,24 @@ feature {NONE} -- Implementation
set_map_name (a_base_selection: DB_EXPRESSION)
-- Store parameters `item' and their `key'.
do
from
parameters.start
until
parameters.after
loop
a_base_selection.set_map_name (parameters.item_for_iteration, parameters.key_for_iteration)
parameters.forth
if attached parameters as l_parameters then
across
l_parameters as ic
loop
a_base_selection.set_map_name (ic.item, ic.key)
end
end
end
unset_map_name (a_base_selection: DB_EXPRESSION)
-- Remove parameters item associated with key `key'.
do
from
parameters.start
until
parameters.after
loop
a_base_selection.unset_map_name (parameters.key_for_iteration)
parameters.forth
if attached parameters as l_parameters then
across
l_parameters as ic
loop
a_base_selection.unset_map_name (ic.key)
end
end
end
@@ -101,26 +98,25 @@ feature {NONE} -- Implementation
-- exclude sensitive information.
do
create Result.make_empty
from
a_parameters.start
until
a_parameters.after
loop
Result.append ("name:")
Result.append (a_parameters.key_for_iteration.as_string_32)
Result.append (", value:")
if
a_parameters.key_for_iteration.has_substring ("Password") or else
a_parameters.key_for_iteration.has_substring ("password")
then
-- Data to exclude
else
if attached a_parameters.item_for_iteration as l_item then
Result.append (l_item.out)
if a_parameters /= Void then
across
a_parameters as ic
loop
Result.append ("name:")
Result.append (ic.key.as_string_32)
Result.append (", value:")
if
ic.key.has_substring ("Password") or else
ic.key.has_substring ("password")
then
-- Data to exclude
else
if attached ic.item as l_item then
Result.append (l_item.out)
end
end
Result.append ("%N")
end
Result.append ("%N")
a_parameters.forth
end
end

View File

@@ -1,8 +1,9 @@
note
description: "Help to encode sql queries, to prevent sql injections."
date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $"
revision: "$Revision: 95678 $"
date: "$Date: 2015-01-27 19:15:02 +0100 (mar., 27 janv. 2015) $"
revision: "$Revision: 96542 $"
EIS: "SQL server injection", "src=http://blogs.msdn.com/b/raulga/archive/2007/01/04/dynamic-sql-sql-injection.aspx", "protocol=url"
expanded class
DATABASE_SQL_SERVER_ENCODER
@@ -12,7 +13,7 @@ inherit
feature -- Escape SQL input
encode (a_string:READABLE_STRING_32): READABLE_STRING_32
encode (a_string: READABLE_STRING_32): READABLE_STRING_32
-- Escape single quote (') and braces ([,]).
local
l_string: STRING

View File

@@ -1,7 +1,7 @@
note
description: "Error from database"
date: "$Date: 2013-08-08 16:39:49 -0300 (ju. 08 de ago. de 2013) $"
revision: "$Revision: 195 $"
date: "$Date: 2014-11-13 16:23:47 +0100 (jeu., 13 nov. 2014) $"
revision: "$Revision: 96085 $"
class
DATABASE_ERROR

View File

@@ -1,7 +1,7 @@
note
description: "Database error handler"
date: "$Date: 2013-08-08 16:39:49 -0300 (ju. 08 de ago. de 2013) $"
revision: "$Revision: 195 $"
date: "$Date: 2014-11-13 16:23:47 +0100 (jeu., 13 nov. 2014) $"
revision: "$Revision: 96085 $"
class
DATABASE_ERROR_HANDLER

View File

@@ -1,8 +1,8 @@
note
description: "Summary description for {DATABASE_NO_CHANGE_ERROR}."
author: ""
date: "$Date$"
revision: "$Revision$"
date: "$Date: 2014-11-13 16:23:47 +0100 (jeu., 13 nov. 2014) $"
revision: "$Revision: 96085 $"
class
DATABASE_NO_CHANGE_ERROR

View File

@@ -1,153 +0,0 @@
note
description: "Provides security routine helpers"
date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $"
revision: "$Revision: 95678 $"
class
SECURITY_PROVIDER
inherit
REFACTORING_HELPER
feature -- Access
token: STRING
-- Cryptographic random base 64 string.
do
Result := salt_with_size (5)
-- Remove trailing equal sign
Result.keep_head (Result.count - 1)
end
salt: STRING
-- Cryptographic random number of 16 bytes.
do
Result := salt_with_size (16)
end
password: STRING
-- Cryptographic random password of 10 bytes.
do
Result := salt_with_size (10)
-- Remove trailing equal signs
Result.keep_head (Result.count - 2)
end
password_hash (a_password, a_salt: STRING): STRING
-- Password hash based on password `a_password' and salt value `a_salt'.
do
Result := sha1_string (a_password + a_salt )
end
feature {NONE} -- Implementation
salt_with_size (a_val: INTEGER): STRING
-- Return a salt with size `a_val'.
local
l_salt: SALT_XOR_SHIFT_64_GENERATOR
l_array: ARRAY [INTEGER_8]
i: INTEGER
do
create l_salt.make (a_val)
create l_array.make_empty
i := 1
across
l_salt.new_sequence as c
loop
l_array.force (c.item.as_integer_8, i)
i := i + 1
end
Result := encode_base_64 (l_array)
end
sha1_string (a_str: STRING): STRING
-- SHA1 diggest of `a_str'.
do
sha1.update_from_string (a_str)
Result := sha1.digest_as_string
sha1.reset
end
sha1: SHA1
-- Create a SHA1 object.
once
create Result.make
end
feature -- Encoding
encode_base_64 (bytes: SPECIAL [INTEGER_8]): STRING_8
-- Encodes a byte array into a STRING doing base64 encoding.
local
l_output: SPECIAL [INTEGER_8]
l_remaining: INTEGER
i, ptr: INTEGER
char: CHARACTER
do
to_implement ("Check existing code to do that!!!.")
create l_output.make_filled (0, ((bytes.count + 2) // 3) * 4)
l_remaining := bytes.count
from
i := 0
ptr := 0
until
l_remaining <= 3
loop
l_output [ptr] := encode_value (bytes [i] |>> 2)
ptr := ptr + 1
l_output [ptr] := encode_value (((bytes [i] & 0x3) |<< 4) | ((bytes [i + 1] |>> 4) & 0xF))
ptr := ptr + 1
l_output [ptr] := encode_value (((bytes [i + 1] & 0xF) |<< 2) | ((bytes [i + 2] |>> 6) & 0x3))
ptr := ptr + 1
l_output [ptr] := encode_value (bytes [i + 2] & 0x3F)
ptr := ptr + 1
l_remaining := l_remaining - 3
i := i + 3
end
-- encode when exactly 1 element (left) to encode
char := '='
if l_remaining = 1 then
l_output [ptr] := encode_value (bytes [i] |>> 2)
ptr := ptr + 1
l_output [ptr] := encode_value (((bytes [i]) & 0x3) |<< 4)
ptr := ptr + 1
l_output [ptr] := char.code.as_integer_8
ptr := ptr + 1
l_output [ptr] := char.code.as_integer_8
ptr := ptr + 1
end
-- encode when exactly 2 elements (left) to encode
if l_remaining = 2 then
l_output [ptr] := encode_value (bytes [i] |>> 2)
ptr := ptr + 1
l_output [ptr] := encode_value (((bytes [i] & 0x3) |<< 4) | ((bytes [i + 1] |>> 4) & 0xF));
ptr := ptr + 1
l_output [ptr] := encode_value ((bytes [i + 1] & 0xF) |<< 2);
ptr := ptr + 1
l_output [ptr] := char.code.as_integer_8
ptr := ptr + 1
end
Result := ""
across
l_output as elem
loop
Result.append_character (elem.item.to_character_8)
end
end
base64_map: SPECIAL [CHARACTER_8]
-- Table for Base64 encoding.
once
Result := ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/").area
end
encode_value (i: INTEGER_8): INTEGER_8
-- Encode `i'.
do
Result := base64_map [i & 0x3F].code.as_integer_8
end
end

View File

@@ -1,53 +0,0 @@
note
description: "Summary description for {STRING_HELPER}."
date: "$Date: 2014-08-08 16:02:11 -0300 (vi., 08 ago. 2014) $"
revision: "$Revision: 95593 $"
class
STRING_HELPER
feature -- Access
is_blank (s: detachable READABLE_STRING_32): BOOLEAN
local
i,n: INTEGER
do
Result := True
if s /= Void then
from
i := 1
n := s.count
until
i > n or not Result
loop
Result := s[i].is_space
i := i + 1
end
end
end
indented_text (pre: READABLE_STRING_8; t: READABLE_STRING_8): READABLE_STRING_8
-- Indendted text.
local
s8: STRING_8
do
s8 := t.string
s8.prepend (pre)
s8.replace_substring_all ("%N", "%N" + pre)
Result := s8
end
json_encode (a_string: STRING): STRING
-- json encode `a_string'.
local
encode: SHARED_JSON_ENCODER
do
create encode
Result := encode.json_encoder.encoded_string (a_string)
debug
print ("%NResult" + Result)
end
end
end