mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	* Move config options to SAML plugin
  This moves all configuration options from Config.java to SAML auth manager. This
  allows us to use the config framework.
* Make SAML2UserAuthenticator validate SAML token in httprequest
* Make logout API use ConfigKeys defined in saml auth manager
* Before doing SAML auth, cleanup local states and cookies
* Fix configurations in 4.5.1 to 4.5.2 upgrade path
* Fail if idp has no sso URL defined
* Add a default set of SAML SP cert for testing purposes
  Now to enable and use saml, one needs to do a deploydb-saml after doing a deploydb
* UI remembers login selections, IDP server
- CLOUDSTACK-8458:
    * On UI show dropdown list of discovered IdPs
    * Support SAML Federation, where there may be more than one IdP
        - New datastructure to hold metadata of SP or IdP
        - Recursive processing of IdP metadata
        - Fix login/logout APIs to get new interface and metadata data structure
        - Add org/contact information to metadata
        - Add new API: listIdps that returns list of all discovered IdPs
        - Refactor and cleanup code and tests
- CLOUDSTACK-8459:
    * Add HTTP-POST binding to SP metadata
    * Authn requests must use either HTTP POST/Artifact binding
- CLOUDSTACK-8461:
    * Use unspecified x509 cert as a fallback encryption/signing key
      In case a IDP's metadata does not clearly say if their certificates need to be
      used as signing or encryption and we don't find that, fallback to use the
      unspecified key itself.
- CLOUDSTACK-8462:
    * SAML Auth plugin should not do authorization
      This removes logic to create user if they don't exist. This strictly now
      assumes that users have been already created/imported/authorized by admins.
      As per SAML v2.0 spec section 4.1.2, the SP provider should create authn requests using
      either HTTP POST or HTTP Artifact binding to transfer the message through a
      user agent (browser in our case). The use of HTTP Redirect was one of the reasons
      why this plugin failed to work for some IdP servers that enforce this.
    * Add new User Source
      By reusing the source field, we can find if a user has been SAML enabled or not.
      The limitation is that, once say a user is imported by LDAP and then SAML
      enabled - they won't be able to use LDAP for authentication
    * UI should allow users to pass in domain they want to log into, though it is
      optional and needed only when a user has accounts across domains with same
      username and authorized IDP server
    * SAML users need to be authorized before they can authenticate
        - New column entity to track saml entity id for a user
        - Reusing source column to check if user is saml enabled or not
        - Add new source types, saml2 and saml2disabled
        - New table saml_token to solve the issue of multiple users across domains and
          to enforce security by tracking authn token and checking the samlresponse for
          the tokens
        - Implement API: authorizeSamlSso to enable/disable saml authentication for a
          user
        - Stubs to implement saml token flushing/expiry
- CLOUDSTACK-8463:
    * Use username attribute specified in global setting
      Use username attribute defined by admin from a global setting
      In case of encrypted assertion/attributes:
      - Decrypt them
      - Check signature if provided to check authenticity of message using IdP's
        public key and SP's private key
      - Loop through attributes to find the username
- CLOUDSTACK-8538:
    * Add new global config for SAML request sig algorithm
- CLOUDSTACK-8539:
    * Add metadata refresh timer task and token expiring
        - Fix domain path and save it to saml_tokens
        - Expire hour old saml tokens
        - Refresh metadata based on timer task
        - Fix unit tests
This closes #489
(cherry picked from commit 20ce346f3acb794b08a51841bab2188d426bf7dc)
Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
Conflicts:
	client/WEB-INF/classes/resources/messages_hu.properties
	plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixCheckHealthCommandWrapper.java
	plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java
	ui/scripts/ui-custom/login.js
		
	
			
		
			
				
	
	
		
			321 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			321 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/cygdrive/c/Python27
 | |
| # Licensed to the Apache Software Foundation (ASF) under one
 | |
| # or more contributor license agreements.  See the NOTICE file
 | |
| # distributed with this work for additional information
 | |
| # regarding copyright ownership.  The ASF licenses this file
 | |
| # to you under the Apache License, Version 2.0 (the
 | |
| # "License"); you may not use this file except in compliance
 | |
| # with the License.  You may obtain a copy of the License at
 | |
| #
 | |
| #   http://www.apache.org/licenses/LICENSE-2.0
 | |
| #
 | |
| # Unless required by applicable law or agreed to in writing,
 | |
| # software distributed under the License is distributed on an
 | |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 | |
| # KIND, either express or implied.  See the License for the
 | |
| # specific language governing permissions and limitations
 | |
| # under the License.
 | |
| 
 | |
| import os
 | |
| import os.path
 | |
| import sys
 | |
| from xml.dom import minidom
 | |
| from xml.parsers.expat import ExpatError
 | |
| 
 | |
| 
 | |
| REGULAR_USER = 'u'
 | |
| DOMAIN_ADMIN = 'd'
 | |
| ROOT_ADMIN = 'r'
 | |
| 
 | |
| user_to_func = {
 | |
|     REGULAR_USER: 'populateForUser',
 | |
|     DOMAIN_ADMIN: 'populateForDomainAdmin',
 | |
|     ROOT_ADMIN: 'populateForRootAdmin',
 | |
|     }
 | |
| 
 | |
| 
 | |
| user_to_cns = {
 | |
|     REGULAR_USER: 'userCommandNames',
 | |
|     DOMAIN_ADMIN: 'domainAdminCommandNames',
 | |
|     ROOT_ADMIN: 'rootAdminCommandNames',
 | |
|     }
 | |
| 
 | |
| 
 | |
| dirname_to_user = {
 | |
|     'regular_user': REGULAR_USER,
 | |
|     'domain_admin': DOMAIN_ADMIN,
 | |
|     'root_admin': ROOT_ADMIN,
 | |
|     }
 | |
| 
 | |
| 
 | |
| dirname_to_dirname = {
 | |
|     'regular_user': 'user',
 | |
|     'domain_admin': 'domain_admin',
 | |
|     'root_admin': 'root_admin',
 | |
|     }
 | |
| 
 | |
| 
 | |
| known_categories = {
 | |
|     'Cisco' : 'External Device',
 | |
|     'SystemVm': 'System VM',
 | |
|     'VirtualMachine': 'Virtual Machine',
 | |
|     'VM': 'Virtual Machine',
 | |
|     'Domain': 'Domain',
 | |
|     'Template': 'Template',
 | |
|     'Iso': 'ISO',
 | |
|     'Volume': 'Volume',
 | |
|     'Vlan': 'VLAN',
 | |
|     'IpAddress': 'Address',
 | |
|     'PortForwarding': 'Firewall',
 | |
|     'Firewall': 'Firewall',
 | |
|     'StaticNat': 'NAT',
 | |
|     'IpForwarding': 'NAT',
 | |
|     'Host': 'Host',
 | |
|     'Cluster': 'Cluster',
 | |
|     'Account': 'Account',
 | |
|     'Snapshot': 'Snapshot',
 | |
|     'User': 'User',
 | |
|     'Os': 'Guest OS',
 | |
|     'ServiceOffering': 'Service Offering',
 | |
|     'DiskOffering': 'Disk Offering',
 | |
|     'LoadBalancer': 'Load Balancer',
 | |
|     'SslCert': 'Load Balancer',
 | |
|     'Router': 'Router',
 | |
|     'SystemVm': 'System VM',
 | |
|     'Configuration': 'Configuration',
 | |
|     'Capabilities': 'Configuration',
 | |
|     'Pod': 'Pod',
 | |
|     'PublicIpRange': 'Network',
 | |
|     'Zone': 'Zone',
 | |
|     'Vmware' : 'Zone',
 | |
|     'NetworkOffering': 'Network Offering',
 | |
|     'NetworkACL': 'Network ACL',
 | |
|     'Network': 'Network',
 | |
|     'CiscoNexus': 'Network',
 | |
|     'OpenDaylight': 'Network',
 | |
|     'createServiceInstance': 'Network',
 | |
|     'addGloboDnsHost': 'Network',
 | |
|     'Vpn': 'VPN',
 | |
|     'Limit': 'Limit',
 | |
|     'ResourceCount': 'Limit',
 | |
|     'CloudIdentifier': 'Cloud Identifier',
 | |
|     'InstanceGroup': 'VM Group',
 | |
|     'StorageMaintenance': 'Storage Pool',
 | |
|     'StoragePool': 'Storage Pool',
 | |
|     'StorageProvider': 'Storage Pool',
 | |
|     'SecurityGroup': 'Security Group',
 | |
|     'SSH': 'SSH',
 | |
|     'register': 'Registration',
 | |
|     'AsyncJob': 'Async job',
 | |
|     'Certificate': 'Certificate',
 | |
|     'Hypervisor': 'Hypervisor',
 | |
|     'Alert': 'Alert',
 | |
|     'Event': 'Event',
 | |
|     'login': 'Authentication',
 | |
|     'logout': 'Authentication',
 | |
|     'saml': 'Authentication',
 | |
|     'getSPMetadata': 'Authentication',
 | |
|     'listIdps': 'Authentication',
 | |
|     'authorizeSamlSso': 'Authentication',
 | |
|     'listSamlAuthorization': 'Authentication',
 | |
|     'Capacity': 'System Capacity',
 | |
|     'NetworkDevice': 'Network Device',
 | |
|     'ExternalLoadBalancer': 'Ext Load Balancer',
 | |
|     'ExternalFirewall': 'Ext Firewall',
 | |
|     'Usage': 'Usage',
 | |
|     'TrafficMonitor': 'Usage',
 | |
|     'TrafficType': 'Usage',
 | |
|     'Product': 'Product',
 | |
|     'LB': 'Load Balancer',
 | |
|     'ldap': 'LDAP',
 | |
|     'Swift': 'Swift',
 | |
|     'S3' : 'S3',
 | |
|     'SecondaryStorage': 'Host',
 | |
|     'Project': 'Project',
 | |
|     'Lun': 'Storage',
 | |
|     'Pool': 'Pool',
 | |
|     'VPC': 'VPC',
 | |
|     'PrivateGateway': 'VPC',
 | |
|     'Simulator': 'simulator',
 | |
|     'StaticRoute': 'VPC',
 | |
|     'Tags': 'Resource tags',
 | |
|     'NiciraNvpDevice': 'Nicira NVP',
 | |
|     'BrocadeVcsDevice': 'Brocade VCS',
 | |
|     'BigSwitchBcfDevice': 'BigSwitch BCF',
 | |
| 	'NuageVsp': 'Nuage VSP',
 | |
|     'AutoScale': 'AutoScale',
 | |
|     'Counter': 'AutoScale',
 | |
|     'Condition': 'AutoScale',
 | |
|     'Api': 'API Discovery',
 | |
|     'Region': 'Region',
 | |
|     'Detail': 'Resource metadata',
 | |
|     'addIpToNic': 'Nic',
 | |
|     'removeIpFromNic': 'Nic',
 | |
|     'listNics':'Nic',
 | |
| 	'AffinityGroup': 'Affinity Group',
 | |
|     'addImageStore': 'Image Store',
 | |
|     'listImageStore': 'Image Store',
 | |
|     'deleteImageStore': 'Image Store',
 | |
|     'createSecondaryStagingStore': 'Image Store',
 | |
|     'deleteSecondaryStagingStore': 'Image Store',
 | |
|     'listSecondaryStagingStores': 'Image Store',
 | |
|     'InternalLoadBalancer': 'Internal LB',
 | |
| 	'DeploymentPlanners': 'Configuration',
 | |
| 	'ObjectStore': 'Image Store',
 | |
|     'PortableIp': 'Portable IP',
 | |
|     'dedicateHost': 'Dedicate Resources',
 | |
|     'releaseDedicatedHost': 'Dedicate Resources',
 | |
|     'Baremetal' : 'Baremetal',
 | |
|     'UCS' : 'UCS',
 | |
|     'Ucs' : 'UCS',
 | |
|     'CacheStores' : 'Cache Stores',
 | |
|     'CacheStore' : 'Cache Store',
 | |
|     'IAM' : 'IAM',
 | |
|     'OvsElement' : 'Ovs Element',
 | |
|     'StratosphereSsp' : ' Stratosphere SSP'
 | |
|     }
 | |
| 
 | |
| 
 | |
| categories = {}
 | |
| 
 | |
| 
 | |
| def choose_category(fn):
 | |
|     for k, v in known_categories.items():
 | |
|         if k in fn:
 | |
|             return v
 | |
|     raise Exception('Need to add a category for %s to %s:known_categories' %
 | |
|                     (fn, __file__))
 | |
|     sys.exit(1)
 | |
| 
 | |
| 
 | |
| for f in sys.argv:
 | |
|     dirname, fn = os.path.split(f)
 | |
|     if not fn.endswith('.xml'):
 | |
|         continue
 | |
|     if fn.endswith('Summary.xml'):
 | |
|         continue
 | |
|     if fn.endswith('SummarySorted.xml'):
 | |
|         continue
 | |
|     if fn == 'alert_types.xml':
 | |
|         continue
 | |
|     if dirname.startswith('./'):
 | |
|         dirname = dirname[2:]
 | |
|     try:
 | |
|         with open(f) as data:
 | |
|             dom = minidom.parse(data)
 | |
|         name = dom.getElementsByTagName('name')[0].firstChild.data
 | |
|         isAsync = dom.getElementsByTagName('isAsync')[0].firstChild.data
 | |
|         category = choose_category(fn)
 | |
|         if category not in categories:
 | |
|             categories[category] = []
 | |
|         categories[category].append({
 | |
|             'name': name,
 | |
|             'dirname': dirname_to_dirname[dirname],
 | |
|             'async': isAsync == 'true',
 | |
|             'user': dirname_to_user[dirname],
 | |
|             })
 | |
|     except ExpatError as e:
 | |
|         pass
 | |
|     except IndexError as e:
 | |
|         print(fn)
 | |
| 
 | |
| 
 | |
| def xml_for(command):
 | |
|     name = command['name']
 | |
|     async = command['async'] and ' (A)' or ''
 | |
|     dirname = command['dirname']
 | |
|     return '''<xsl:if test="name=\'%(name)s\'">
 | |
| <li><a href="%(dirname)s/%(name)s.html"><xsl:value-of select="name"/>%(async)s</a></li>
 | |
| </xsl:if>
 | |
| ''' % locals()
 | |
| 
 | |
| 
 | |
| def write_xml(out, user):
 | |
|     with open(out, 'w') as f:
 | |
|         cat_strings = []
 | |
| 
 | |
|         for category in categories.keys():
 | |
|             strings = []
 | |
|             for command in categories[category]:
 | |
|                 if command['user'] == user:
 | |
|                     strings.append(xml_for(command))
 | |
|             if strings:
 | |
|                 all_strings = ''.join(strings)
 | |
|                 cat_strings.append((len(strings), category, all_strings))
 | |
| 
 | |
|         cat_strings.sort(reverse=True)
 | |
| 
 | |
|         i = 0
 | |
|         for _1, category, all_strings in cat_strings:
 | |
|             if i == 0:
 | |
|                 f.write('<div class="apismallsections">\n')
 | |
|             f.write('''<div class="apismallbullet_box">
 | |
| <h5>%(category)s</h5>
 | |
| <ul>
 | |
| <xsl:for-each select="commands/command">
 | |
| %(all_strings)s
 | |
| </xsl:for-each>
 | |
| </ul>
 | |
| </div>
 | |
| 
 | |
| ''' % locals())
 | |
|             if i == 3:
 | |
|                 f.write('</div>\n')
 | |
|                 i = 0
 | |
|             else:
 | |
|                 i += 1
 | |
|         if i != 0:
 | |
|             f.write('</div>\n')
 | |
| 
 | |
| 
 | |
| def java_for(command, user):
 | |
|     name = command['name']
 | |
|     cns = user_to_cns[user]
 | |
|     return '''%(cns)s.add("%(name)s");
 | |
| ''' % locals()
 | |
| 
 | |
| 
 | |
| def java_for_user(user):
 | |
|     strings = []
 | |
|     for category in categories.keys():
 | |
|         for command in categories[category]:
 | |
|             if command['user'] == user:
 | |
|                 strings.append(java_for(command, user))
 | |
|     func = user_to_func[user]
 | |
|     all_strings = ''.join(strings)
 | |
|     return '''
 | |
|     public void %(func)s() {
 | |
|         %(all_strings)s
 | |
|     }
 | |
| ''' % locals()
 | |
| 
 | |
| 
 | |
| def write_java(out):
 | |
|     with open(out, 'w') as f:
 | |
|         f.write('''/* Generated using gen_toc.py.  Do not edit. */
 | |
| 
 | |
| import java.util.HashSet;
 | |
| import java.util.Set;
 | |
| 
 | |
| public class XmlToHtmlConverterData {
 | |
| 
 | |
| 	Set<String> rootAdminCommandNames = new HashSet<String>();
 | |
| 	Set<String> domainAdminCommandNames = new HashSet<String>();
 | |
| 	Set<String> userCommandNames = new HashSet<String>();
 | |
| 
 | |
| ''')
 | |
|         f.write(java_for_user(REGULAR_USER) + "\n");
 | |
|         f.write(java_for_user(ROOT_ADMIN) + "\n")
 | |
|         f.write(java_for_user(DOMAIN_ADMIN) + "\n")
 | |
| 
 | |
|         f.write('''
 | |
| }
 | |
| 
 | |
| ''')
 | |
| 
 | |
| 
 | |
| write_xml('generatetocforuser_include.xsl', REGULAR_USER)
 | |
| write_xml('generatetocforadmin_include.xsl', ROOT_ADMIN)
 | |
| write_xml('generatetocfordomainadmin_include.xsl', DOMAIN_ADMIN)
 | |
| write_java('XmlToHtmlConverterData.java')
 |