From bb99bb49636db1d3f07b6f540dc8624a677ade2f Mon Sep 17 00:00:00 2001 From: mr Date: Mon, 6 Dec 2010 12:09:06 +0100 Subject: insert basic a&a libs to development branch --- lib/authorization.rb | 291 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/environment.rb | 2 + lib/helper.rb | 72 ++++++++++--- lib/opentox-ruby.rb | 2 +- lib/policy.rb | 242 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 592 insertions(+), 17 deletions(-) create mode 100644 lib/authorization.rb create mode 100644 lib/policy.rb diff --git a/lib/authorization.rb b/lib/authorization.rb new file mode 100644 index 0000000..0cba96a --- /dev/null +++ b/lib/authorization.rb @@ -0,0 +1,291 @@ +module OpenTox + + #Module for Authorization and Authentication + #@example Authentication + # require "opentox-ruby-api-wrapper" + # OpenTox::Authorization::AA_SERVER = "https://opensso.in-silico.ch" #if not set in .opentox/conf/[environment].yaml + # token = OpenTox::Authorization.authenticate("benutzer", "passwort") + #@see http://www.opentox.org/dev/apis/api-1.2/AA OpenTox A&A API 1.2 specification + + module Authorization + + #Helper Class AA to create and send default policies out of xml templates + #@example Creating a default policy to a URI + # aa=OpenTox::Authorization::AA.new(tok) + # xml=aa.get_xml('http://uri....') + # OpenTox::Authorization.create_policy(xml,tok) + + class AA + attr_accessor :user, :token_id, :policy + + #Generates AA object - requires token_id + # @param [String] token_id + def initialize(token_id) + @user = Authorization.get_user(token_id) + @token_id = token_id + @policy = Policies.new() + end + + #Cleans AA Policies and loads default xml file into policy attribute + #set uri and user, returns Policyfile(XML) for open-sso + # @param [String] URI to create a policy for + def get_xml(uri) + @policy.drop_policies + @policy.load_default_policy(@user, uri) + return @policy.to_xml + end + + #Loads and sends Policyfile(XML) to open-sso server + # @param [String] URI to create a policy for + def send(uri) + xml = get_xml(uri) + ret = false + ret = Authorization.create_policy(xml, @token_id) + LOGGER.debug "Policy send with token_id: #{@token_id}" + LOGGER.warn "Not created Policy is: #{xml}" if !ret + ret + end + + end + + #Returns the open-sso server set in the config file .opentox/config/[environment].yaml + # @return [String, nil] the openSSO server URI or nil + def self.server + return AA_SERVER + end + + #Authentication against OpenSSO. Returns token. Requires Username and Password. + # @param [String, String]Username,Password + # @return [String, nil] gives token_id or nil + def self.authenticate(user, pw) + return true if !AA_SERVER + begin + resource = RestClient::Resource.new("#{AA_SERVER}/auth/authenticate") + out = resource.post(:username=>user, :password => pw).sub("token.id=","").sub("\n","") + return out + rescue + return nil + end + end + + #Logout on opensso. Make token invalid. Requires token + # @param [String]token_id the token_id + # @return [Boolean] true if logout is OK + def self.logout(token_id) + begin + resource = RestClient::Resource.new("#{AA_SERVER}/auth/logout") + resource.post(:subjectid => token_id) + return true + rescue + return false + end + end + + #Authorization against OpenSSO for a URI with request-method (action) [GET/POST/PUT/DELETE] + # @param [String,String,String]uri,action,token_id + # @return [Boolean, nil] returns true, false or nil (if authorization-request fails). + def self.authorize(uri, action, token_id) + return true if !AA_SERVER + begin + resource = RestClient::Resource.new("#{AA_SERVER}/auth/authorize") + return true if resource.post(:uri => uri, :action => action, :subjectid => token_id) == "boolean=true\n" + rescue + return nil + end + end + + #Checks if a token is a valid token + # @param [String]token_id token_id from openSSO session + # @return [Boolean] token_id is valid or not. + def self.is_token_valid(token_id) + return true if !AA_SERVER + begin + resource = RestClient::Resource.new("#{AA_SERVER}/auth/isTokenValid") + return true if resource.post(:tokenid => token_id) == "boolean=true\n" + rescue + return false + end + end + + #Returns array with all policies of the token owner + # @param [String]token_id requires token_id + # @return [Array, nil] returns an Array of policy names or nil if request fails + def self.list_policies(token_id) + begin + resource = RestClient::Resource.new("#{AA_SERVER}/pol") + out = resource.get(:subjectid => token_id) + return out.split("\n") + rescue + return nil + end + end + + #Returns a policy in xml-format + # @param [String, String]policy,token_id + # @return [String] XML of the policy + def self.list_policy(policy, token_id) + begin + resource = RestClient::Resource.new("#{AA_SERVER}/pol") + return resource.get(:subjectid => token_id,:id => policy) + rescue + return nil + end + end + + #Returns the owner (who created the first policy) of an URI + # @param [String, String]uri,token_id + # return [String, nil]owner,nil returns owner of the URI + def self.get_uri_owner(uri, token_id) + begin + resource = RestClient::Resource.new("#{AA_SERVER}/pol") + return resource.get(:uri => uri, :subjectid => token_id).sub("\n","") + rescue + return nil + end + end + + #Checks if a policy exists to a URI. Requires URI and token. + # @param [String, String]uri,token_id + # return [Boolean] + def self.uri_has_policy(uri, token_id) + owner = get_uri_owner(uri, token_id) + return true if owner and owner != "null" + false + end + + #List all policynames for a URI. Requires URI and token. + # @param [String, String]uri,token_id + # return [Array, nil] returns an Array of policy names or nil if request fails + def self.list_uri_policies(uri, token_id) + begin + resource = RestClient::Resource.new("#{AA_SERVER}/pol") + out = resource.get(:uri => uri, :polnames => true, :subjectid => token_id) + policies = []; notfirstline = false + out.split("\n").each do |line| + policies << line if notfirstline + notfirstline = true + end + return policies + rescue + return nil + end + end + + #Sends a policy in xml-format to opensso server. Requires policy-xml and token. + # @param [String, String]policyxml,token_id + # return [Boolean] returns true if policy is created + def self.create_policy(policy, token_id) + begin +# resource = RestClient::Resource.new("#{AA_SERVER}/Pol/opensso-pol") + LOGGER.debug "OpenTox::Authorization.create_policy policy: #{policy[168,43]} with token:" + token_id.to_s + " length: " + token_id.length.to_s +# return true if resource.post(policy, :subjectid => token_id, :content_type => "application/xml") + return true if RestClientWrapper.post("#{AA_SERVER}/pol", {:subjectid => token_id, :content_type => "application/xml"}, policy) + rescue + return false + end + end + + #Deletes a policy + # @param [String, String]policyname,token_id + # @return [Boolean,nil] + def self.delete_policy(policy, token_id) + begin + resource = RestClient::Resource.new("#{AA_SERVER}/pol") + LOGGER.debug "OpenTox::Authorization.delete_policy policy: #{policy} with token: #{token_id}" + return true if resource.delete(:subjectid => token_id, :id => policy) + rescue + return nil + end + end + + #Returns array of all possible LDAP-Groups + # @param [String]token_id + # @return [Array] + def self.list_groups(token_id) + begin + resource = RestClient::Resource.new("#{AA_SERVER}/opensso/identity/search") + grps = resource.post(:admin => token_id, :attributes_names => "objecttype", :attributes_values_objecttype => "group") + grps.split("\n").collect{|x| x.sub("string=","")} + rescue + [] + end + end + + #Returns array of the LDAP-Groups of an user + # @param [String]token_id + # @return [Array] gives array of LDAP groups of a user + def self.list_user_groups(user, token_id) + begin + resource = RestClient::Resource.new("#{AA_SERVER}/opensso/identity/read") + out = resource.post(:name => user, :admin => token_id, :attributes_names => "group") + grps = [] + out.split("\n").each do |line| + grps << line.sub("identitydetails.group=","") if line.include?("identitydetails.group=") + end + return grps + rescue + [] + end + end + + #Returns the owner (user id) of a token + # @param [String]token_id + # @return [String]user + def self.get_user(token_id) + begin + resource = RestClient::Resource.new("#{AA_SERVER}/opensso/identity/attributes") + out = resource.post(:subjectid => token_id, :attributes_names => "uid") + user = ""; check = false + out.split("\n").each do |line| + if check + user = line.sub("userdetails.attribute.value=","") if line.include?("userdetails.attribute.value=") + check = false + end + check = true if line.include?("userdetails.attribute.name=uid") + end + return user + rescue + nil + end + end + + #Send default policy with Authorization::AA class + # @param [String, String]URI,token_id + def self.send_policy(uri, token_id) + return true if !AA_SERVER + aa = Authorization::AA.new(token_id) + ret = aa.send(uri) + LOGGER.debug "OpenTox::Authorization send policy for URI: #{uri} | token_id: #{token_id} - policy created: #{ret}" + ret + end + + #Deletes all policies of an URI + # @param [String, String]URI,token_id + # @return [Boolean] + def self.delete_policies_from_uri(uri, token_id) + policies = list_uri_policies(uri, token_id) + policies.each do |policy| + ret = delete_policy(policy, token_id) + LOGGER.debug "OpenTox::Authorization delete policy: #{policy} - with result: #{ret}" + end + return true + end + + #Checks (if token_id is valid) if a policy exist and create default policy if not + def self.check_policy(uri, token_id) + token_valid = OpenTox::Authorization.is_token_valid(token_id) + LOGGER.debug "OpenTox::Authorization.check_policy with uri: #{uri}, token_id: #{token_id} is valid: #{token_valid}" + if uri and token_valid + if !uri_has_policy(uri, token_id) + return send_policy(uri, token_id) + else + LOGGER.debug "OpenTox::Authorization.check_policy URI: #{uri} has already a Policy." + end + end + true + end + + end +end + + diff --git a/lib/environment.rb b/lib/environment.rb index 4f1cc80..1761d92 100644 --- a/lib/environment.rb +++ b/lib/environment.rb @@ -83,6 +83,8 @@ class OwlNamespace end +AA_SERVER = CONFIG[:authorization] ? (CONFIG[:authorization][:server] ? CONFIG[:authorization][:server] : nil) : nil + RDF = OwlNamespace.new 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' OWL = OwlNamespace.new 'http://www.w3.org/2002/07/owl#' DC = OwlNamespace.new 'http://purl.org/dc/elements/1.1/' diff --git a/lib/helper.rb b/lib/helper.rb index a9f451e..b69f9b4 100644 --- a/lib/helper.rb +++ b/lib/helper.rb @@ -1,26 +1,66 @@ helpers do # Authentification - def protected! - response['WWW-Authenticate'] = %(Basic realm="Testing HTTP Auth") and \ + def protected!(token_id) + if env["session"] + flash[:notice] = "You don't have access to this section: " and \ + redirect back and \ + return unless authorized?(token_id) + end throw(:halt, [401, "Not authorized\n"]) and \ - return unless authorized? + return unless authorized?(token_id) end - - def authorized? - @auth ||= Rack::Auth::Basic::Request.new(request.env) - @auth.provided? && @auth.basic? && @auth.credentials && @auth.credentials == ['api', API_KEY] + + def authorized?(token_id) + case request.env['REQUEST_METHOD'] + when "DELETE", "PUT" + ret = OpenTox::Authorization.authorize(request.env['SCRIPT_URI'], request.env['REQUEST_METHOD'], token_id) + LOGGER.debug "OpenTox helpers OpenTox::Authorization authorized? method: #{request.env['REQUEST_METHOD']} , URI: #{request.env['SCRIPT_URI']}, token_id: #{token_id} with return #{ret}." + return ret + when "POST" + if OpenTox::Authorization.is_token_valid(token_id) + LOGGER.debug "OpenTox helpers OpenTox::Authorization.is_token_valid: true" + return true + end + LOGGER.warn "OpenTox helpers POST on #{request.env['SCRIPT_URI']} with token_id: #{token_id} false." + end + LOGGER.debug "Not authorized for: 1. #{request['SCRIPT_URI']} 2. #{request.env['SCRIPT_URI']} with Method: #{request.env['REQUEST_METHOD']} with Token #{token_id}" + LOGGER.debug "Request infos: #{request.inspect}" + return false end - -=begin - def xml(object) - builder do |xml| - xml.instruct! - object.to_xml - end - end -=end + def unprotected_requests + case env['REQUEST_URI'] + when /\/login$|\/logout$|\/predict$|\/upload$/ + return true + when /\/compound|\/feature|\/task|\/toxcreate/ #to fix: read from config | validation should be protected + return true + else + return false + end + end + + def check_token_id(token_id) + return false if !token_id + return true if token_id.size > 62 + false + end +end +before do + + unless unprotected_requests or env['REQUEST_METHOD'] == "GET" + begin + token_id = session[:token_id] if session[:token_id] + token_id = params[:token_id] if params[:token_id] and !check_token_id(token_id) + token_id = request.env['HTTP_TOKEN_ID'] if request.env['HTTP_TOKEN_ID'] and !check_token_id(token_id) + # see http://rack.rubyforge.org/doc/SPEC.html + rescue + LOGGER.debug "OpenTox api wrapper: helper before filter: NO token_id." + token_id = "" + end + protected!(token_id) if AA_SERVER + end + end diff --git a/lib/opentox-ruby.rb b/lib/opentox-ruby.rb index 9f9ff26..c0bff95 100644 --- a/lib/opentox-ruby.rb +++ b/lib/opentox-ruby.rb @@ -8,6 +8,6 @@ rescue LoadError puts "Please install Openbabel with 'rake openbabel:install' in the compound component" end -['opentox', 'compound','dataset', 'parser','serializer', 'algorithm','model','task','validation','feature', 'rest_client_wrapper'].each do |lib| +['opentox', 'compound','dataset', 'parser','serializer', 'algorithm','model','task','validation','feature', 'rest_client_wrapper', 'authorization', 'policy', 'helper'].each do |lib| require lib end diff --git a/lib/policy.rb b/lib/policy.rb new file mode 100644 index 0000000..0ef8298 --- /dev/null +++ b/lib/policy.rb @@ -0,0 +1,242 @@ +module OpenTox + require "rexml/document" + + #Module for policy-processing + # @see also http://www.opentox.org/dev/apis/api-1.2/AA for opentox API specs + # Class Policies corresponds to container of an xml-policy-fle + class Policies + + attr_accessor :name, :policies + + def initialize() + @policies = {} + end + + #create new policy instance with name + # @param [String]name of the policy + def new_policy(name) + @policies[name] = Policy.new(name) + end + + #drop a specific policy in a policies instance + # @param [String]name of the policy + # @return [Boolean] + def drop_policy(name) + return true if @policies.delete(name) + end + + #drop all policies in a policies instance + def drop_policies + @policies.each do |name, policy| + drop_policy(name) + end + return true + end + + #loads a default policy template in policies instance + def load_default_policy(user, uri, group="member") + template = case user + when "guest", "anonymous" then "default_guest_policy" + else "default_policy" + end + xml = File.read(File.join(File.dirname(__FILE__), "templates/#{template}.xml")) + self.load_xml(xml) + datestring = Time.now.strftime("%Y-%m-%d-%H-%M-%S-x") + rand(1000).to_s + + @policies["policy_user"].name = "policy_user_#{user}_#{datestring}" + @policies["policy_user"].rules["rule_user"].uri = uri + @policies["policy_user"].rules["rule_user"].name = "rule_user_#{user}_#{datestring}" + @policies["policy_user"].subjects["subject_user"].name = "subject_user_#{user}_#{datestring}" + @policies["policy_user"].subjects["subject_user"].value = "uid=#{user},ou=people,dc=opentox,dc=org" + @policies["policy_user"].subject_group = "subjects_user_#{user}_#{datestring}" + + @policies["policy_group"].name = "policy_group_#{group}_#{datestring}" + @policies["policy_group"].rules["rule_group"].uri = uri + @policies["policy_group"].rules["rule_group"].name = "rule_group_#{group}_#{datestring}" + @policies["policy_group"].subjects["subject_group"].name = "subject_group_#{group}_#{datestring}" + @policies["policy_group"].subjects["subject_group"].value = "cn=#{group},ou=groups,dc=opentox,dc=org" + @policies["policy_group"].subject_group = "subjects_#{group}_#{datestring}" + return true + end + + #loads a xml template + def load_xml(xml) + rexml = REXML::Document.new(xml) + rexml.elements.each("Policies/Policy") do |pol| #Policies + policy_name = pol.attributes["name"] + new_policy(policy_name) + #@policies[policy_name] = Policy.new(policy_name) + rexml.elements.each("Policies/Policy[@name='#{policy_name}']/Rule") do |r| #Rules + rule_name = r.attributes["name"] + uri = rexml.elements["Policies/Policy[@name='#{policy_name}']/Rule[@name='#{rule_name}']/ResourceName"].attributes["name"] + @policies[policy_name].rules[rule_name] = @policies[policy_name].new_rule(rule_name, uri) + rexml.elements.each("Policies/Policy[@name='#{policy_name}']/Rule[@name='#{rule_name}']/AttributeValuePair") do |attribute_pairs| + action=nil; value=nil; + attribute_pairs.each_element do |elem| + action = elem.attributes["name"] if elem.attributes["name"] + value = elem.text if elem.text + end + if action and value + case action + when "GET" + @policies[policy_name].rules[rule_name].get = value + when "POST" + @policies[policy_name].rules[rule_name].post = value + when "PUT" + @policies[policy_name].rules[rule_name].put = value + when "DELETE" + @policies[policy_name].rules[rule_name].delete = value + end + end + end + end + rexml.elements.each("Policies/Policy[@name='#{policy_name}']/Subjects") do |subjects| #Subjects + @policies[policy_name].subject_group = subjects.attributes["name"] + rexml.elements.each("Policies/Policy[@name='#{policy_name}']/Subjects[@name='#{@policies[policy_name].subject_group}']/Subject") do |s| #Subject + subject_name = s.attributes["name"] + subject_type = s.attributes["type"] + subject_value = rexml.elements["Policies/Policy[@name='#{policy_name}']/Subjects[@name='#{@policies[policy_name].subject_group}']/Subject[@name='#{subject_name}']/AttributeValuePair/Value"].text + @policies[policy_name].new_subject(subject_name, subject_type, subject_value) if subject_name and subject_type and subject_value + end + end + end + end + + #generates xml from policies instance + def to_xml + doc = REXML::Document.new() + doc << REXML::DocType.new("Policies", "PUBLIC \"-//Sun Java System Access Manager7.1 2006Q3\n Admin CLI DTD//EN\" \"jar://com/sun/identity/policy/policyAdmin.dtd\"") + doc.add_element(REXML::Element.new("Policies")) + + @policies.each do |name, pol| + policy = REXML::Element.new("Policy") + policy.attributes["name"] = pol.name + policy.attributes["referralPolicy"] = false + policy.attributes["active"] = true + @policies[name].rules.each do |r,rl| + rule = @policies[name].rules[r] + out_rule = REXML::Element.new("Rule") + out_rule.attributes["name"] = rule.name + servicename = REXML::Element.new("ServiceName") + servicename.attributes["name"]="iPlanetAMWebAgentService" + out_rule.add_element(servicename) + rescourcename = REXML::Element.new("ResourceName") + rescourcename.attributes["name"] = rule.uri + out_rule.add_element(rescourcename) + + ["get","post","delete","put"].each do |act| + if rule.method(act).call + attribute = REXML::Element.new("Attribute") + attribute.attributes["name"] = act.upcase + attributevaluepair = REXML::Element.new("AttributeValuePair") + attributevaluepair.add_element(attribute) + attributevalue = REXML::Element.new("Value") + attributevaluepair.add_element(attributevalue) + attributevalue.add_text REXML::Text.new(rule.method(act).call) + out_rule.add_element(attributevaluepair) + + end + end + policy.add_element(out_rule) + end + + subjects = REXML::Element.new("Subjects") + subjects.attributes["name"] = pol.subject_group + subjects.attributes["description"] = "" + @policies[name].subjects.each do |subj, subjs| + subject = REXML::Element.new("Subject") + subject.attributes["name"] = pol.subjects[subj].name + subject.attributes["type"] = pol.subjects[subj].type + subject.attributes["includeType"] = "inclusive" + attributevaluepair = REXML::Element.new("AttributeValuePair") + attribute = REXML::Element.new("Attribute") + attribute.attributes["name"] = "Values" + attributevaluepair.add_element(attribute) + attributevalue = REXML::Element.new("Value") + attributevalue.add_text REXML::Text.new(pol.subjects[subj].value) + attributevaluepair.add_element(attributevalue) + subject.add_element(attributevaluepair) + subjects.add_element(subject) + end + policy.add_element(subjects) + doc.root.add_element(policy) + end + out = "" + doc.write(out, 2) + return out + end + + end + + #single policy in a policies instance + class Policy + + attr_accessor :name, :rules, :subject_group, :subjects + + def initialize(name) + @name = name + @rules = {} + @subject_group = "" + @subjects = {} + end + + #create a new rule instance for the policy + def new_rule(name, uri) + @rules[name] = Rule.new(name, uri) + end + + #create a new subject instance for the policy + def new_subject(name, type, value) + @subjects[name] = Subject.new(name, type, value) + end + + #rule inside a policy + class Rule + + attr_accessor :name, :uri, :get, :post, :put, :delete + + def initialize(name, uri) + @name = name + @uri = uri + end + + def rename(new, old) + self[new] = self.delete(old) + self[new].name = new + end + + def get=(value) + @get = check_value(value, @get) + end + + def post=(value) + @post = check_value(value, @post) + end + + def delete=(value) + @delete = check_value(value, @delete) + end + + def put=(value) + @put = check_value(value, @put) + end + + private + #checks if value is allow or deny. returns old value if not valid. + def check_value(new_value, old_value) + return (new_value=="allow" || new_value=="deny" || new_value==nil) ? new_value : old_value + end + end + + class Subject + + attr_accessor :name, :type, :value + + def initialize(name, type, value) + @name = name + @type = type + @value = value + end + end + end +end \ No newline at end of file -- cgit v1.2.3