changeset 73:2f31ecabe9ea

doc security
author wizard
date Mon, 29 Mar 2010 06:56:05 +0400
parents eac47fa4f262
children 84aa8c395fce
files Lib/IMPL/Config.pm Lib/IMPL/Object/Factory.pm Lib/IMPL/Security.pm Lib/IMPL/Security/Auth.pm Lib/IMPL/Security/Auth/Simple.pm Lib/IMPL/Security/Role.pm Lib/IMPL/Web/Application.pm Lib/IMPL/Web/QueryHandler/AuthCookie.pm Lib/IMPL/Web/QueryHandler/SecureCookie.pm Lib/IMPL/Web/Security.pm Lib/IMPL/Web/Security/Embed.pm Lib/IMPL/Web/Security/Session.pm _doc/make.pl
diffstat 13 files changed, 358 insertions(+), 135 deletions(-) [+]
line wrap: on
line diff
--- a/Lib/IMPL/Config.pm	Fri Mar 26 16:26:31 2010 +0300
+++ b/Lib/IMPL/Config.pm	Mon Mar 29 06:56:05 2010 +0400
@@ -118,28 +118,34 @@
 
 =pod
 
-=h1 SYNOPSIS
+=head1 NAME
+
+C<IMPL::Config> - базовый класс для настраиваемого приложения.
+
+=head1 SYNOPSIS
 
-package App::Config
-use base qw(IMPL::Config)
+=begin code
+
+# define application
+
+package MyApp;
+use base qw(IMPL::Config);
 
 use IMPL::Class::Property;
 use IMPL::Config::Class;
 
 BEGIN {
     public property SimpleString => prop_all;
-    public property MyClass => prop_all;
     public property DataSource => prop_all; 
 }
 
 sub CTOR {
     my $this = shift;
-
-    $this->MyClass(new IMPL::Config::Class(Type => 'MyClass'')) unless $this->MyClass;
+    
     $this->DataSource(
     	new IMPL::Config::Activator(
-    		type => 'MyDataSource',
-    		args=>{
+    		factory => 'MyDataSource',
+    		parameters=>{
     			host => 'localhost',
     			user => 'dbuser'
     		}
@@ -147,37 +153,85 @@
     ) unless $this->Exists('DataSource');
 }
 
-# in some script
+# using application object
 
-my $app = spawn App::Config('default.xml');
+my $app = spawn MyApp('default.xml');
 
 $app->Run();
 
+=end code
+
+Ниже приведен пример файла C<default.xml> содержащего настройки приложения
+
+=begin code xml
+
+<app type='MyApp'>
+	<SimpleString>The application</SimpleString>
+	<DataSource type='IMPL::Config::Activator'>
+		<factory>MyDataSourceClass</factory>
+		<parameters type='HASH'>
+			<host>localhost</host>
+			<user>dbuser</user>
+		</parameters>
+	</DataSource>
+</app>
+
+=end code xml
+
 =head1 DESCRIPTION
 
-Позволяет сохранить/загрузить конфигурацию. Также все классы конфигурации
-должны наследоваться от данного класса, и все Public свойства будут
-автоматически сохраняться и восстанавливаться.
+C<[Serializable]>
+
+C<[Autofill]>
+
+C<use base IMPL::Object::Accessor>
+
+Базовый класс для приложений. Использует подход, что приложение
+является объектом, состояние которого предтавляет собой конфигурацию,
+а методы - логику.
+
+Данный класс реализует функционал десериализации (и сериализации) экземпляра
+приложения из XML документа. Для этого используется механизм C<IMPL::Serialization>.
+При этом используются опции C<IMPL::Serialization::XmlFormatter> C<IdentOutput> и
+C<SkipWhitespace> для записи документа в легко читаемом виде.
+
+Поскольку в результате восстановления приложения восстанавливаются все элементы
+из файла конфигурации, то это может потребовать значительных ресурсов для
+создания частей, которые могут никогда не понадобиться. Например, не требуется инициализация
+источника данных для передачи пользователю статических данных, сохраненных на диске.
+
+Для решения этой проблемы используются специальные объекты C<IMPL::Config::Activator>.
+
+Если у приложения описано свойство, в котором хранится C<IMPL::Config::Activator>, то
+при первом обращении к такому свойству, будет создан объект вызовом метода
+C<< IMPL::Config::Activator->activate() >> и возвращен как значение этого свойства.
+Таким образом реализуется прозрачная отложенная активация объектов, что позволяет
+экономить ресурсы. 
 
 =head1 MEMBERS
 
 =over
 
-=item C<< IMPL::Config->LoadXMLFile($fileName) >>
+=item C<[static] LoadXMLFile($fileName) >
 
-Создает из XML файла экземпляр приложения
+Создает из XML файла C<$fileName> экземпляр приложения
 
-=item C<< $instance->SaveXMLFile($fileName) >>
+=item C<SaveXMLFile($fileName)>
 
-Сохраняет приложение в файл
+Сохраняет приложение в файл C<$fileName>
+
+=item C<[get] xml >
 
-=item C<< xml >>
+Сохраняет конфигурацию приложения в XML строку.
 
-Сохраняет конфигурацию приложения в XML строку
+=item C<[static,operator] spawn($file)>
 
-=item C<< IMPL::Config->spawn($file) >>
+Синоним для C<LoadXMLFile>, предполагается использование как оператора.
 
-Синоним для C<LoadXMLFile>
+=item C<rawGet($propname,...)>
+
+Метод для получения значений свойств приложения. Данный метод позволяет избежать
+использование активации объектов через C<IMPL::Config::Activator>.
 
 =back
 
--- a/Lib/IMPL/Object/Factory.pm	Fri Mar 26 16:26:31 2010 +0300
+++ b/Lib/IMPL/Object/Factory.pm	Mon Mar 29 06:56:05 2010 +0400
@@ -7,7 +7,8 @@
 
 BEGIN {
 	public property factory => prop_get | owner_set;
-	public property parameters => prop_get | owner_set; 
+	public property parameters => prop_get | owner_set;
+	public property method => prop_get | owner_set; 
 }
 
 # custom factory, overrides default
@@ -18,10 +19,11 @@
 }
 
 sub CTOR {
-	my ($this,$factory,$parameters) = @_;
+	my ($this,$factory,$parameters,$method) = @_;
 	
 	$this->factory($factory) or die new IMPL::InvalidArgumentException("The argument 'factory' is mandatory");
 	$this->parameters($parameters) if $parameters;
+	$this->method($method) if $method;
 }
 
 # override default restore method
@@ -41,7 +43,11 @@
 sub CreateObject {
 	my $this = shift;
 	
-	return $this->factory->new($this->parameters ? (_as_list($this->parameters),@_) : @_);
+	if (my $method = $this->method) {
+		$this->factory->$method($this->parameters ? (_as_list($this->parameters),@_) : @_);	
+	} else {
+		$this->factory->new($this->parameters ? (_as_list($this->parameters),@_) : @_);		
+	}
 }
 
 sub _as_list {
@@ -122,9 +128,9 @@
 
 =over
 
-=item C< CTOR($factory,$parameters) >
+=item C< CTOR($factory,$parameters,$method) >
 
-Создает новый экземпляр
+Создает новый экземпляр фабрики.
 
 =over
 
@@ -145,6 +151,11 @@
 Если является любым другим объектом или скаляром, то будет передан параметром методу
 C<new> как есть.
 
+=item C<$method>
+
+Имя метода (или ссылка на процедуру), который будет вызван у C<$factory> при создании
+текущей фабрикой нового объекта.
+
 =back
 
 =item C< [get] factory >
@@ -168,7 +179,9 @@
 sub new {
 	my ($this,@params) = @_;
 	
-	return $this->factory->new(_as_list($this->parameters), @params);
+	my $method = $this->method || 'new';
+	
+	return $this->factory->$method(_as_list($this->parameters), @params);
 }
 
 =end code
--- a/Lib/IMPL/Security.pm	Fri Mar 26 16:26:31 2010 +0300
+++ b/Lib/IMPL/Security.pm	Mon Mar 29 06:56:05 2010 +0400
@@ -22,6 +22,16 @@
 	die new IMPL::NotImplementedException();
 }
 
+sub MakeContext {
+	my ($this,$principal,$refRoles,$auth) = @_;
+	
+	return new IMPL::Security::Context(
+		principal => $principal,
+		rolesAssigned => $refRoles,
+		auth => $auth
+	);
+}
+
 sub Rules {
 	return \@rules;
 }
@@ -129,6 +139,26 @@
 
 =back
 
+=item C<MakeContext($principal,$role,$auth)>
+
+Создает контекст безопасности, инициализируя его передданными параметрами.
+
+=over
+
+=item C<$principal>
+
+Объект пользователя
+
+=item C<$role>
+
+Роль или ссылка на массив ролей
+
+=item C<$auth>
+
+Объект аутентификации
+
+=back
+
 =item C<Take($principal,$role)>
 
 Метод. Делегирует текущему пользователю полномочия другого пользователя. При этом выполняется проверка
@@ -150,7 +180,7 @@
 
 =back
 
-=item C<Roles()>
+=item C<Rules()>
 
 Возвращает список правил которые выполняются при проверках доступа. Пререопределите этот
 метод, чтобы возвращать собственный список правил. Список правил является ссылкой на массив
--- a/Lib/IMPL/Security/Auth.pm	Fri Mar 26 16:26:31 2010 +0300
+++ b/Lib/IMPL/Security/Auth.pm	Mon Mar 29 06:56:05 2010 +0400
@@ -28,6 +28,10 @@
 	die new IMPL::NotImplementedException;
 }
 
+sub isTrusted {
+	0;
+}
+
 1;
 
 __END__
@@ -86,6 +90,10 @@
 Создает пакет для авторизации на основе данных безопасности для пользователя.
 C<$SecData> - Зависит от пакета аутентификации.
 
+=item C<[get] isTrusted>
+
+Флаг того, что аутентификация закончена успешно.
+
 =item C<DoAuth($Challenge)>
 
 Производит аутентификацию пользователя и инициализацию сессии,
@@ -103,6 +111,8 @@
 =item C<[static] SecDataArgs()>
 
 Возвращает хеш с описанием параметров для функции C<CreateSecData>. Ключами являются
-имена параметров, значениями - типы. 
+имена параметров, значениями - типы.
+
+=back 
 
 =cut
\ No newline at end of file
--- a/Lib/IMPL/Security/Auth/Simple.pm	Fri Mar 26 16:26:31 2010 +0300
+++ b/Lib/IMPL/Security/Auth/Simple.pm	Mon Mar 29 06:56:05 2010 +0400
@@ -80,7 +80,28 @@
 
 Результат либо C<AUTH_SUCCESS>, либо C<AUTH_FAIL>
 
-=item
+=item C<$challenge>
+
+В случае успеха возвращает cookie (уникальный номер) сессии
+
+=back
+
+=item C<ValidateSession($challenge)>
+
+Проверяет аутентичность сессии. Использует один этап. C<$challenge> cookie
+сессии, полученный при выполнении метода C<DoAuth>.
+
+Возвращает C<($status,$challenge)>
+
+=over
+
+=item C<$status>
+
+Результат либо C<AUTH_SUCCESS>, либо C<AUTH_FAIL>
+
+=item C<$challenge>
+
+Всегда C<undef>
 
 =back
 
--- a/Lib/IMPL/Security/Role.pm	Fri Mar 26 16:26:31 2010 +0300
+++ b/Lib/IMPL/Security/Role.pm	Mon Mar 29 06:56:05 2010 +0400
@@ -5,8 +5,8 @@
 use IMPL::Class::Property;
 
 BEGIN {
-	public property roleName => prop_get;
-	public property parentRoles => prop_get;
+	public property roleName => prop_get | owner_set;
+	public property parentRoles => prop_get | owner_set | prop_list;
 }
 
 sub CTOR {
@@ -39,20 +39,24 @@
 
 =pod
 
+=head1 NAME
+
+C<IMPL::Security::Role> Роль
+
 =head1 DESCRIPTION
 
-Роль. Может включать в себя базовые роли.
+Может включать в себя базовые роли.
 Имеется метод для проверки наличия необходимых ролей в текущей роли.
 
 =head1 MEMBERS
 
 =over
 
-=item C<roleName>
+=item C<[get] roleName>
 
 Имя роли, ее идентификатор
 
-=item C<parentRoles>
+=item C<[get,list] parentRoles>
 
 Список родительских ролей
 
--- a/Lib/IMPL/Web/Application.pm	Fri Mar 26 16:26:31 2010 +0300
+++ b/Lib/IMPL/Web/Application.pm	Mon Mar 29 06:56:05 2010 +0400
@@ -17,16 +17,10 @@
     public property actionFactory => prop_all;
     public property handlersQuery => prop_all | prop_list;
     public property responseCharset => prop_all;
+    public property security => prop_all;
     public property options => prop_all;
 }
 
-# custom factory
-sub new {
-    my ($self,$file) = @_;
-    
-    return $self->LoadXMLFile($file);
-}
-
 sub CTOR {
 	my ($this) = @_;
 	
@@ -84,6 +78,8 @@
 
 =head1 DESCRIPTION
 
+C< use base qw( IMPL::Config IMPL::Object::Singleton )>
+
 Зкземпляр приложения содержит в себе глобальные настройки, реализует контроллер запросов,
 в качестве источника запросов используется CGI или иной совместимый модуль.
 
@@ -180,3 +176,47 @@
 
 =end code xml
 
+=head1 MEMBERS
+
+=over
+
+=item C<[get,set] handlerError>
+
+Обработчик который будет вызван в случае возникновения необработанной ошибки
+в процессе работы приложения. После чего приложение корректно завершается.
+
+=item C<[get,set] actionFactory>
+
+Фабрика объектов, которая используется приложением, для создания объектов
+типа C<IMPL::Web::Application::Action> при обработки C<CGI> запросов.
+
+=begin code
+
+my $action = $this->actionFactory->new(
+    query => $query,
+    application => $this, 
+);
+
+=end code
+
+=item C< [get,set,list] handlersQuery >
+
+Список обработчиков запросов, которые будут переданы созданному объекту-действию.
+
+=item C< [get,set] responseCharset>
+
+Кодировка ответа клиенту.
+
+=item C< [get,set] security >
+
+Объект C<IMPL::Web::Security>, для работы с инфраструктурой безопасности.
+
+=item C< [get,set] options >
+
+Обычно ссылка на хеш с настраиваемыми объектами, используется для возможности
+програмной настройки активаторов, т.к. напрямую через свойства приложения получить
+к ним доступ не получится.
+ 
+=back
+
+=cut
\ No newline at end of file
--- a/Lib/IMPL/Web/QueryHandler/AuthCookie.pm	Fri Mar 26 16:26:31 2010 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-package IMPL::Web::QueryHandler::AuthCookie;
-
-use base qw(IMPL::Web::QueryHandler);
-use Digest::MD5 qw(md5_hex);
-
-use IMPL::Class::Property;
-use IMPL::Security::Auth qw(:Const);
-
-BEGIN {
-	public property salt => prop_all;
-}
-
-sub CTOR {
-	my ($this) = @_;
-	
-	
-}
-
-sub Process {
-	my ($this,$action,$nextHandler) = @_;
-	
-	my $method = $action->query->cookie('method') || 'simple';
-	
-	if ($method eq 'simple') {
-		
-		my $sid = $action->query->cookie('sid'); 
-		
-		if ($action->query->cookie('sign') eq md5_hex(
-			$this->salt,
-			$sid,
-			$this->salt
-		) ) {
-			
-			my $context = $action->application->security->Session(
-				id => $sid,
-				method => 'simple'				
-			);
-			
-			my ($result,$challenge) = $context->auth->ValidateSession($sid);
-			
-			if ($result == AUTH_SUCCESS) {
-				return $context->Impersonate($nextHandler);				
-			} else {
-				return $nextHandler->();
-			}
-		}
-	} else {
-		die new IMPL::Exception("Unknown auth method",$method);
-	}
-}
-
-
-1;
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/IMPL/Web/QueryHandler/SecureCookie.pm	Mon Mar 29 06:56:05 2010 +0400
@@ -0,0 +1,52 @@
+package IMPL::Web::QueryHandler::SecureCookie;
+
+use base qw(IMPL::Web::QueryHandler);
+use Digest::MD5 qw(md5_hex);
+
+use IMPL::Class::Property;
+use IMPL::Security::Auth qw(:Const);
+
+BEGIN {
+	public property salt => prop_all;
+}
+
+sub CTOR {
+	my ($this) = @_;
+	
+	
+}
+
+sub Process {
+	my ($this,$action,$nextHandler) = @_;
+	
+	my $method = $action->query->cookie('method') || 'simple';
+	
+	if ($method eq 'simple') {
+		
+		my $sid = $action->query->cookie('sid'); 
+		
+		if ($action->query->cookie('sign') eq md5_hex(
+			$this->salt,
+			$sid,
+			$this->salt
+		) ) {
+			
+			my $context = $action->application->security->Session(
+				id => $sid				
+			);
+			
+			my ($result,$challenge) = $context->auth->ValidateSession($sid);
+			
+			if ($result == AUTH_SUCCESS) {
+				return $context->Impersonate($nextHandler);				
+			} else {
+				return $nextHandler->();
+			}
+		}
+	} else {
+		die new IMPL::Exception("Unknown auth method",$method);
+	}
+}
+
+
+1;
\ No newline at end of file
--- a/Lib/IMPL/Web/Security.pm	Fri Mar 26 16:26:31 2010 +0300
+++ b/Lib/IMPL/Web/Security.pm	Mon Mar 29 06:56:05 2010 +0400
@@ -1,29 +1,72 @@
 package IMPL::Web::Security;
 
+use base qw(IMPL::Object IMPL::Security IMPL::Object::Autofill);
+
+use IMPL::Class::Property;
+
+__PACKAGE__->PassThroughArgs;
+
+BEGIN {
+	public property sessionFactory => prop_get | owner_set;
+	public property userFactory => prop_get | owner_set; 
+}
+
+sub CTOR {
+	my ($this) = @_;
+	
+	$this->dataSource or die new IMPL::InvalidArgumentException("The argument is required",'dataSource');
+}
+
+sub Session {
+	my ($this,%args) = @_;
+}
+
+sub User {
+	
+}
+
 1;
 
 __END__
 
 =pod
 
+=head1 NAME
+
+C<IMPL::Web::Security> Модуль для аутентификации и авторизации веб запроса.
+
+=head1 SINOPSYS
+
+=begin code xml
+
+<security type='IMPL::Config::Activator'>
+	<factory>IMPL::Web::Security</factory>
+	<parameters type='HASH'>
+		<sessionFactory type='IMPL::Object::Factory'>
+			<factory type='IMPL::Object::Factory'>App::Data::Session</factory>
+			<method>insert</method>
+		</sessionFactory>
+	</parameters>
+</security>
+
+=end code xml
+
 =head1 DESCRIPTION
 
-Модуль для аутентификации и авторизации веб запроса.
-
-Получает запрос, вычленяет из него информацию для авторизации, производит авторизацию пользователя
-и создает контекст безопасности.
-
-При этом использует указанные модули аутентификации.
-
-Информацию для аутентификации модуль получает через соответствующие адаптеры.
+Отвечает за инфраструктуру аутентификации и авторизации запросов. Основная особенность
+заключается в том, что запросы приходят через значительные интевалы времени, хотя и
+относятся к одной логической транзакции. В промежутках между запросами сервер не
+сохраняет свое состояние. Поэтому при каждом обращении сервер восстанавливает
+контекст безопасности.
 
-=item C<IMPL::Web::Security::Server>
-
-Аутентифицирует пользователя на основе данных, предоставленных сервером
+C<IMPL::Web::Session> Объект обеспечивающий сохранение состояния в рамках одной сессии
+пользователя. Кроме контекста безопасности хранит дополнительние данные, которые необходимо
+сохранить между обработкой запросов.
 
-=item C<IMPL::Web::Security::Embed>
+C<IMPL::Web::User> Объект, устанавливающий связь между идентификатором пользователя
+C<IMPL::Security::Principal>, его ролями и данными безопасности для создания объектов
+аутентификации C<IMPL::Security::Auth>.
 
-Аутентифицирует пользователя используя указанные модули, при этом получает данные
-из запроса.
+=head1 MEMBERS
 
 =cut
\ No newline at end of file
--- a/Lib/IMPL/Web/Security/Embed.pm	Fri Mar 26 16:26:31 2010 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-package IMPL::Web::Security::Embed;
-
-use base qw(IMPL::Object);
-
-1;
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/IMPL/Web/Security/Session.pm	Mon Mar 29 06:56:05 2010 +0400
@@ -0,0 +1,14 @@
+package IMPL::Web::Security::Session;
+
+use base qw(IMPL::Object);
+
+use IMPL::Class::Property;
+
+__PACKAGE__->PassThroughArgs;
+
+BEGIN {
+	public property id => prop_all;
+	public property securityContext => prop_all;
+}
+
+1;
\ No newline at end of file
--- a/_doc/make.pl	Fri Mar 26 16:26:31 2010 +0300
+++ b/_doc/make.pl	Mon Mar 29 06:56:05 2010 +0400
@@ -158,31 +158,31 @@
 sub view_seq_link {
 	my ($self,$text) = @_;
 	
-	if ($text =~ /^\s*(?:(\w+)\s+)?(\w+(?:\:\:\w+)+)\s*$/) {
+	$text =~ s/(\w+(?:\:\:\w+)+)/
+		if (my $url = $self->mk_filelink($1)) {
+			"<a href='$url'>$1<\/a>";
+		} else {
+			$1;
+		}
+	/gex;
 	
-		my ($keyword, $package) = ($1 || '',$2);
-		
-		my @path = split /::/, $package;
-		
-		pop @path if $keyword;		
-		shift @path if uc $path[0] eq 'IMPL';
-		
+	return "<code>$text</code>";
+}
+
+sub mk_filelink {
+	my ($self,$package) = @_;
+	
+	return undef unless $package; 
+	
+	my @path = split /::/, $package;
+	
+	if ($path[0] eq 'IMPL') {
+		shift @path;
 		if (-f File::Spec->catfile($LibDir,@path).".pm") {
-			return '<code><a href="'. '../'x($level-1) . File::Spec->catfile(@path) .'.html">'.$text.'</a></code>';			
-		}
-		
-	} elsif ($text =~ /^\s*(\w+(?:\:\:\w+)+)\s*-&gt;\s*(\w+)\s*$/) {
-		my ($package,$member) = ($1,$2);
-		
-		my @path = split /::/, $package;
-		shift @path;
-		
-		if (-f File::Spec->catfile($LibDir,@path).".pm") {
-			return '<code><a href="'. '../'x($level-1) . File::Spec->catfile(@path) .'.html">'.$text.'</a></code>';			
+			return '../'x($level-1) . File::Spec->catfile(@path).'.html';
 		}
 	}
-	
-	return "<code>$text</code>";
+	return undef;	
 }
 
 sub view_seq_code {