timfel-active_cmis 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2010 XAOP bvba
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+
6
+ files (the "Software"), to deal in the Software without
7
+ restriction, including without limitation the rights to use,
8
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the
10
+
11
+ Software is furnished to do so, subject to the following
12
+ conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23
+
24
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # ActiveCMIS Release 0.3.1 #
2
+ **Homepage**: <http://xaop.com/labs/activecmis>
3
+ **Git**: <http://github.com/xaop/activecmis>
4
+ **Documentation**: <http://rdoc.info/github/xaop/activecmis/master/frames>
5
+ **Author**: XAOP bvba
6
+ **Copyright**: 2011
7
+ **License**: MIT License
8
+ ## Synopsis ##
9
+ ActiveCMIS is Ruby library aimed at easing the interaction with various CMIS providers. It creates Ruby objects for CMIS objects, and creates Ruby classes that correspond to CMIS types.
10
+ ## Features ##
11
+ - Read support for all CMIS object types
12
+ - Write support and the ability to create new objects.
13
+ - Support for paging
14
+
15
+ ## Changes since 0.2.6 ##
16
+ The way authentication works has changed. If you previously used ActiveCMIS.connect then you're fine, otherwise the authentication changes will affect you: the authenticate methods on ActiveCMIS::Server and ActiveCMIS::Repository now return a new object, and don't change the authentication on the object itself. You can also specify optional authentication when connecting to a Server, or when calling the repository method.
17
+
18
+ ## Installation ##
19
+ If you haven't installed Nokogiri yet it will be installed automatically, you will need a C compiler and the development files for libxml2.
20
+
21
+ > gem install active_cmis
22
+
23
+ ActiveCMIS also depends on ntlm-http for ntlm authentication, unfortunately ntlm-http is broken on ruby 1.9.2
24
+ ## Usage ##
25
+ require 'active_cmis'
26
+ repository = ActiveCMIS.load_config('configuration', 'optional_filename_for_config')
27
+ f = repository.root_folder
28
+ p f.items.map do |i| i.cmis.name end
29
+
30
+ And so on ...
31
+
32
+ Full documentation of the API can be found at [rdoc.info](http://rdoc.info/projects/xaop/activecmis)
33
+
34
+ A tutorial can be found at [the xaop site](http://xaop.com/labs/activecmis "tutorial")
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ require 'rubygems'
2
+
3
+ begin
4
+ require 'yard'
5
+
6
+ YARD::Rake::YardocTask.new do |t|
7
+ t.files = ['lib/**/*.rb', '-', 'TODO'] # optional
8
+ t.options = ["--default-return", "::Object", "--query", "!@private", "--hide-void-return"]
9
+ end
10
+ rescue LoadError
11
+ puts "Yard, or a dependency, not available. Install it with gem install yard"
12
+ end
13
+
14
+ begin
15
+ require 'jeweler'
16
+ require './lib/active_cmis/version'
17
+ Jeweler::Tasks.new do |gemspec|
18
+ gemspec.name = "active_cmis"
19
+ gemspec.version = ActiveCMIS::Version::STRING
20
+ gemspec.summary = "A library to interact with CMIS repositories through the AtomPub/REST binding"
21
+ gemspec.description = "A CMIS library implementing both reading and updating capabilities through the AtomPub/REST binding to CMIS."
22
+ gemspec.email = "joeri@xaop.com"
23
+ gemspec.homepage = "http://xaop.com/labs/activecmis/"
24
+ gemspec.authors = ["Joeri Samson"]
25
+
26
+ gemspec.add_runtime_dependency 'nokogiri', '>= 1.4.1'
27
+ gemspec.add_runtime_dependency 'ntlm-http', '~> 0.1.1'
28
+ gemspec.add_runtime_dependency 'require_relative', '~> 1.0.2'
29
+
30
+ gemspec.required_ruby_version = '>= 1.8.6'
31
+ gemspec.files.exclude '.gitignore'
32
+ end
33
+ Jeweler::GemcutterTasks.new
34
+ rescue LoadError
35
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
36
+ end
data/TODO ADDED
@@ -0,0 +1,7 @@
1
+ - For {ActiveCMIS::Rendition#get_file Rendition#get_file}:
2
+ * filename should be optional, a temporary file should be created (e.g. in /tmp) if no filename is given
3
+ - For {ActiveCMIS::Type::ClassMethods#attributes Type::ClassMethods#attributes}: inherited parameter is misleading (+ default is wrong, some other code depends on it being true by default if it would work)
4
+ - improve use of correct Exceptions:
5
+ * don't throw RuntimeError anymore (except perhaps where the library code is the only code that could cause the exception)
6
+ * try to use correct exceptions when receiving errors from server (may need some wrapper with repository specific code)
7
+ - improve support for queries (i.e. allow interface such as the with_ATTR_NAME way of ActiveDCTM, allow select to return only a few attributes, ...)
@@ -0,0 +1,77 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{timfel-active_cmis}
8
+ s.version = "0.3.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Joeri Samson"]
12
+ s.date = %q{2012-02-15}
13
+ s.description = %q{A CMIS library implementing both reading and updating capabilities through the AtomPub/REST binding to CMIS.}
14
+ s.email = %q{joeri@xaop.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.md",
18
+ "TODO"
19
+ ]
20
+ s.files = [
21
+ "LICENSE",
22
+ "README.md",
23
+ "Rakefile",
24
+ "TODO",
25
+ "active_cmis.gemspec",
26
+ "lib/active_cmis.rb",
27
+ "lib/active_cmis/acl.rb",
28
+ "lib/active_cmis/acl_entry.rb",
29
+ "lib/active_cmis/active_cmis.rb",
30
+ "lib/active_cmis/atomic_types.rb",
31
+ "lib/active_cmis/attribute_prefix.rb",
32
+ "lib/active_cmis/collection.rb",
33
+ "lib/active_cmis/document.rb",
34
+ "lib/active_cmis/exceptions.rb",
35
+ "lib/active_cmis/folder.rb",
36
+ "lib/active_cmis/internal/caching.rb",
37
+ "lib/active_cmis/internal/connection.rb",
38
+ "lib/active_cmis/internal/utils.rb",
39
+ "lib/active_cmis/ns.rb",
40
+ "lib/active_cmis/object.rb",
41
+ "lib/active_cmis/policy.rb",
42
+ "lib/active_cmis/property_definition.rb",
43
+ "lib/active_cmis/query_result.rb",
44
+ "lib/active_cmis/rel.rb",
45
+ "lib/active_cmis/relationship.rb",
46
+ "lib/active_cmis/rendition.rb",
47
+ "lib/active_cmis/repository.rb",
48
+ "lib/active_cmis/server.rb",
49
+ "lib/active_cmis/type.rb",
50
+ "lib/active_cmis/version.rb"
51
+ ]
52
+ s.homepage = %q{http://xaop.com/labs/activecmis/}
53
+ s.require_paths = ["lib"]
54
+ s.required_ruby_version = Gem::Requirement.new(">= 1.8.6")
55
+ s.rubygems_version = %q{1.3.7}
56
+ s.summary = %q{A library to interact with CMIS repositories through the AtomPub/REST binding}
57
+
58
+ if s.respond_to? :specification_version then
59
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
60
+ s.specification_version = 3
61
+
62
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
63
+ s.add_runtime_dependency(%q<nokogiri>, [">= 1.4.1"])
64
+ s.add_runtime_dependency(%q<ntlm-http>, ["~> 0.1.1"])
65
+ s.add_runtime_dependency(%q<require_relative>, ["~> 1.0.2"])
66
+ else
67
+ s.add_dependency(%q<nokogiri>, [">= 1.4.1"])
68
+ s.add_dependency(%q<ntlm-http>, ["~> 0.1.1"])
69
+ s.add_dependency(%q<require_relative>, ["~> 1.0.2"])
70
+ end
71
+ else
72
+ s.add_dependency(%q<nokogiri>, [">= 1.4.1"])
73
+ s.add_dependency(%q<ntlm-http>, ["~> 0.1.1"])
74
+ s.add_dependency(%q<require_relative>, ["~> 1.0.2"])
75
+ end
76
+ end
77
+
@@ -0,0 +1,181 @@
1
+ module ActiveCMIS
2
+ # ACLs belong to a document and have no identity of their own
3
+ #
4
+ # = Updating:
5
+ # The effect on documents other than the one this ACL belongs to depends
6
+ # on the repository.
7
+ #
8
+
9
+ class Acl
10
+ include Internal::Caching
11
+
12
+ # @return [Object] The document or object from which we got this ACL
13
+ attr_reader :document
14
+ # @return [Repository]
15
+ attr_reader :repository
16
+
17
+ # @private
18
+ def initialize(repository, document, link, _data = nil)
19
+ @repository = repository
20
+ @document = document
21
+ @self_link = case link
22
+ when URI; link
23
+ else URI(link)
24
+ end
25
+ @data = _data if _data
26
+ end
27
+
28
+ # Returns an array with all Acl entries.
29
+ # @return [Array<AclEntry>]
30
+ def permissions
31
+ data.xpath("c:permission", NS::COMBINED).map do |permit|
32
+ principal = nil
33
+ permissions = []
34
+ direct = false
35
+ permit.children.each do |child|
36
+ next unless child.namespace && child.namespace.href == NS::CMIS_CORE
37
+
38
+ case child.name
39
+ when "principal"
40
+ child.children.map do |n|
41
+ next unless n.namespace && n.namespace.href == NS::CMIS_CORE
42
+
43
+ if n.name == "principalId" && principal.nil?
44
+ principal = convert_principal(n.text)
45
+ end
46
+ end
47
+ when "permission"
48
+ permissions << child.text
49
+ when "direct"
50
+ direct = AtomicType::Boolean.xml_to_bool(child.text)
51
+ end
52
+ end
53
+ AclEntry.new(principal, permissions, direct)
54
+ end
55
+ end
56
+ cache :permissions
57
+
58
+ # An indicator that the ACL fully describes the permissions for this object.
59
+ # This means that there are no other security constraints.
60
+ def exact?
61
+ @exact ||= begin
62
+ value = data.xpath("c:exact", NS::COMBINED)
63
+ if value.empty?
64
+ false
65
+ elsif value.length == 1
66
+ AtomicType::Boolean.xml_to_bool(value.first.text)
67
+ else
68
+ raise "Unexpected multiplicity of exactness ACL"
69
+ end
70
+ end
71
+ end
72
+
73
+ # @param [String, :anonymous, :world] user Can be "cmis:user" to indicate the currently logged in user.
74
+ # For :anonymous and :world you can use both the the active_cmis symbol or the name used by the CMIS repository
75
+ # @param permissions (see ActiveCMIS::AclEntry#initialize)
76
+ # @return [void]
77
+ def grant_permission(user, *permissions)
78
+ principal = convert_principal(user)
79
+
80
+ relevant = self.permissions.select {|p| p.principal == principal && p.direct?}
81
+ if relevant = relevant.first
82
+ self.permissions.delete relevant
83
+ permissions.concat(relevant.permissions)
84
+ end
85
+
86
+ @updated = true
87
+ self.permissions << AclEntry.new(principal, permissions, true)
88
+ end
89
+
90
+ # @param (see ActiveCMIS::Acl#grant_permission)
91
+ # @return [void]
92
+ def revoke_permission(user, *permissions)
93
+ principal = convert_principal(user)
94
+
95
+ keep = self.permissions.reject {|p| p.principal == principal && p.permissions.any? {|t| permissions.include? t} }
96
+
97
+ relevant = self.permissions.select {|p| p.principal == principal && p.permissions.any? {|t| permissions.include? t} }
98
+ changed = relevant.map {|p| AclEntry.new(principal, p.permissions - permissions, p.direct?) }
99
+
100
+ @updated = true
101
+ @permissions = keep + changed
102
+ end
103
+
104
+ # @param user (see ActiveCMIS::Acl#grant_permission)
105
+ # @return [void]
106
+ def revoke_all_permissions(user)
107
+ principal = convert_principal(user)
108
+ @updated = true
109
+ permissions.reject! {|p| p.principal == principal}
110
+ end
111
+
112
+ # Needed to actually execute changes on the server, this method is also executed when you save an object with a modified ACL
113
+ # @return [void]
114
+ def apply
115
+ body = Nokogiri::XML::Builder.new do |xml|
116
+ xml.acl("xmlns" => NS::CMIS_CORE) do
117
+ permissions.each do |permission|
118
+ xml.permission do
119
+ xml.principal { xml.principalId { convert_principal(permission.principal) }}
120
+ xml.direct { permission.direct? }
121
+ permission.each do |permit|
122
+ xml.permission { permit }
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ conn.put(self_link("onlyBasicPermissions" => false), body)
129
+ reload
130
+ end
131
+
132
+ # True if there are local changes to the ACL
133
+ def updated?
134
+ @updated
135
+ end
136
+
137
+ # @return [void]
138
+ def reload
139
+ @updated = false
140
+ @exact = nil
141
+ __reload
142
+ end
143
+
144
+ private
145
+ def self_link(options = {})
146
+ Internal::Utils.append_parameters(@self_link, options)
147
+ end
148
+
149
+ def conn
150
+ repository.conn
151
+ end
152
+
153
+ def data
154
+ conn.get_xml(self_link).xpath("c:acl", NS::COMBINED)
155
+ end
156
+ cache :data
157
+
158
+ def anonymous_user
159
+ repository.anonymous_user
160
+ end
161
+ def world_user
162
+ repository.world_user
163
+ end
164
+
165
+ def convert_principal(principal)
166
+ case principal
167
+ when :anonymous
168
+ anonymous_user
169
+ when :world
170
+ world
171
+ when anonymous_user
172
+ :anonymous
173
+ when world_user
174
+ :world
175
+ else
176
+ principal
177
+ end
178
+ end
179
+
180
+ end
181
+ end
@@ -0,0 +1,26 @@
1
+ module ActiveCMIS
2
+ class AclEntry
3
+ # @param [<String>] permissions A list of permissions, valid values depend on the repository
4
+ # @private
5
+ def initialize(principal, permissions, direct)
6
+ @principal = principal.freeze
7
+ @permissions = permissions.freeze
8
+ @permissions.each {|p| p.freeze}
9
+ @direct = direct
10
+ end
11
+
12
+
13
+ # Normal users are represented with a string, a non-logged in user is known
14
+ # as :anonymous, the principal :world represents the group of all logged in
15
+ # users.
16
+ # @return [String, :world, :anonymous]
17
+ attr_reader :principal
18
+ # @return [<String>] A frozen array of strings with the permissions
19
+ attr_reader :permissions
20
+
21
+ # True if this is the direct representation of the ACL from the repositories point of view. This means there are no hidden differences that can't be expressed within the limitations of CMIS
22
+ def direct?
23
+ @direct
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,87 @@
1
+ module ActiveCMIS
2
+ # Default logger: no output
3
+ # @return [Logger]
4
+ def self.default_logger
5
+ @default_logger ||= Logger.new(nil)
6
+ end
7
+
8
+ # server_url and repository_id are required options
9
+ #
10
+ # server_login, server_password and server_auth can be used to authenticate against the server,
11
+ # server_auth is optional and defaults to :basic
12
+ #
13
+ # You can also authenticate to the repository, by replacing server_ with repository_, by default
14
+ # the repository will use the same authentication parameters as the server
15
+ #
16
+ # The amoung of logging can be configured by setting log_level (default WARN), this can be done either
17
+ # by naming a Logger::Severity constant or the equivalent integer
18
+ #
19
+ # The destination of the logger output can be set with log_file (defaults to STDOUT), (should not contain ~)
20
+ #
21
+ # Default locations for the config file are: ./cmis.yml and .cmis.yml in that order
22
+ # @return [Repository]
23
+ def self.connect(config)
24
+ if config.is_a? Hash
25
+ if config.has_key? "log_file"
26
+ trace_file = config["log_file"]
27
+ if trace_file == "-"
28
+ trace_file = STDOUT
29
+ end
30
+ logger = Logger.new(trace_file)
31
+ else
32
+ logger = default_logger
33
+ end
34
+ if config.has_key? "log_level"
35
+ logger.level = (Logger.const_get(config["log_level"].upcase) rescue config["log_level"].to_i)
36
+ else
37
+ logger.level = Logger::WARN
38
+ end
39
+
40
+ if user_name = config["server_login"] and password = config["server_password"]
41
+ auth_type = config["server_auth"] || :basic
42
+ authentication_info = [auth_type, user_name, password]
43
+ end
44
+ server = Server.new(config["server_url"], logger, authentication_info)
45
+ if user_name = config["repository_login"] and password = config["repository_password"]
46
+ auth_type = config["repository_auth"] || :basic
47
+ authentication_info = [auth_type, user_name, password]
48
+ end
49
+ repository = server.repository(config["repository_id"], authentication_info)
50
+ return repository
51
+ else
52
+ raise "Configuration does not have correct format (#{config.class} is not a hash)"
53
+ end
54
+ end
55
+
56
+ # Will search for a given configuration in a file, and return the equivalent Repository
57
+ #
58
+ # The options that can be used are the same as for the connect method
59
+ #
60
+ # Default locations for the config file are: ./cmis.yml and .cmis.yml in that order
61
+ # @return [Repository]
62
+ def self.load_config(config_name, file = nil)
63
+ if file.nil?
64
+ ["cmis.yml", File.join(ENV["HOME"], ".cmis.yml")].each do |sl|
65
+ if File.exist?(sl)
66
+ file ||= sl
67
+ end
68
+ end
69
+ if file.nil?
70
+ raise "No configuration provided, and none found in standard locations"
71
+ end
72
+ elsif !File.exist?(file)
73
+ raise "Configuration file #{file} does not exist"
74
+ end
75
+
76
+ config = YAML.load_file(file)
77
+ if config.is_a? Hash
78
+ if config = config[config_name]
79
+ connect(config)
80
+ else
81
+ raise "Configuration not found in file"
82
+ end
83
+ else
84
+ raise "Configuration file #{file} does not have right format (not a hash)"
85
+ end
86
+ end
87
+ end