diff --git a/build/cloudid/root/opt/yunion/share/saml/sp-metadata/aliyun_role.xml b/build/cloudid/root/opt/yunion/share/saml/sp-metadata/aliyun_role.xml new file mode 100644 index 0000000000..bf18325c84 --- /dev/null +++ b/build/cloudid/root/opt/yunion/share/saml/sp-metadata/aliyun_role.xml @@ -0,0 +1,47 @@ + + + + + + + MIIDXjCCAkagAwIBAgIEXHToLjANBgkqhkiG9w0BAQsFADBwMQswCQYDVQQGEwJDTjERMA8GA1UE + CBMISGFuZ3pob3UxKTAnBgNVBAsTIEFsaWJhYmEgQ2xvdWQgQ29tcHV0aW5nIENvLiBMdGQuMSMw + IQYDVQQDExp1cm46YWxpYmFiYTpjbG91ZGNvbXB1dGluZzAgFw0xOTAyMjYwNzE4MDZaGA8yMTE5 + MDIwMjA3MTgwNlowcDELMAkGA1UEBhMCQ04xETAPBgNVBAgTCEhhbmd6aG91MSkwJwYDVQQLEyBB + bGliYWJhIENsb3VkIENvbXB1dGluZyBDby4gTHRkLjEjMCEGA1UEAxMadXJuOmFsaWJhYmE6Y2xv + dWRjb21wdXRpbmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDjD53ZENEHoYNXAOf + OIbVBJhj7SCWKmdjnbnxq8WAFWEeZtS6hZLPpWh1z7b0NjJkvCf5oFVqBJYbbW5kEKV+9CpV6VHZ + qOXmsIRlkvZB+Wnc3SduwiiiUR9JojSPxVQvSf4WLT+HDASlrBztuRV2vHj9utLbvy+6bgVBqF8g + emL9Pcif1robDH8HlqUcADXLAt18E4MbToldVoHjpFc6fAKUXujWH5feAL8g0CKlmf/JVlHLEtu4 + vKPxBQ8sgkysk6EnrjXl6Q4a4t+vbPG5uczA1ouTkDupMCRlaWHIHaJL/AoDGabn8sVXdaVJUKC5 + 54FNkRznBhRQll+Nuc2rAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAE8k4S4HvOglthJwF3aMQXGi + LKW6Becs9SljA0/5VtZQDrDf2By/1BIMvWfZ/dFnO+MylDLVdS6XWvWat/DW0fOGxU4s1WNfshX7 + DJDGR2G1XgtGoDZEYIDahUp5katAPypCkY57fGZlI0d3nq46/2qT/Zpne+pFE3DI/x8klZMniniw + YjNXbG96y/M4DYi1J7RR8mLIfVvz5o1SMGT4Ta2p/USE2M9F6O7/zc2j62dQgXiYa9OONo31RiXR + TmvGEUNuQoBZhVrFIvOnNjIfFT7Xd3CUowwJKP1floserrx4B5jScRAi9yK3x2a2lhfc+PksXfTs + aqIr5TQL4OorLEE= + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName + urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName + urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos + urn:oasis:names:tc:SAML:2.0:nameid-format:entity + + + Alibaba Cloud Console Single Sign-On + + + + + + + Alibaba Cloud Computing Co. Ltd. + AlibabaCloud + https://www.aliyun.com + + diff --git a/build/cloudid/root/opt/yunion/share/saml/sp-metadata/aws.xml b/build/cloudid/root/opt/yunion/share/saml/sp-metadata/aws.xml new file mode 100644 index 0000000000..7e65b9debc --- /dev/null +++ b/build/cloudid/root/opt/yunion/share/saml/sp-metadata/aws.xml @@ -0,0 +1,67 @@ + + + + + + + MIIDbTCCAlWgAwIBAgIEFWIThjANBgkqhkiG9w0BAQsFADBnMR8wHQYDVQQDExZ1 +cm46YW1hem9uOndlYnNlcnZpY2VzMSIwIAYDVQQKExlBbWF6b24gV2ViIFNlcnZp +Y2VzLCBJbmMuMRMwEQYDVQQIEwpXYXNoaW5ndG9uMQswCQYDVQQGEwJVUzAeFw0y +MDA2MDIwMDAwMDBaFw0yMTA2MDIwMDAwMDBaMGcxHzAdBgNVBAMTFnVybjphbWF6 +b246d2Vic2VydmljZXMxIjAgBgNVBAoTGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAgTCldhc2hpbmd0b24xCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwKA4yDh4wAjlkhjJoXARGX9DQWsGTgp9GzoX +NebntY+DjB8H6hIkYRR3++yEDY90qldbZzOaOKRm2jyL7i+MgNt+ktx6wYGD7HBg +e8Z0Zh6eHRIwKbvu4/heMLdqqu+hlXkrC15T4N/LqiwT5JhPTrO4V1KG3ljR1ONG +7aLblVH2aRENV+X9la/AVAPesUpCnoUcTkuCJrcwHrv6hqWmadW8pJy/ISCCM4Fe +JuOAJD0whxg0qRFvE6xcdnKBTWjR03VHqJnyYQGVUKXOlgj8HiKO0JaJkoUAs5ox +WCRBZwSQW+qbP1VKSUfFeBtTMx/TSg4nBRYHinego/e8O1mu6wIDAQABoyEwHzAd +BgNVHQ4EFgQUz1SuekLVxYNvvLpUDRXc/gp2WdAwDQYJKoZIhvcNAQELBQADggEB +AIkUzM9MXr11vp9gBOOXyqyj7sBgoUQHW3mEfKD3DkEOZWUPW5UI7NM9ZHvhdOwd +C1noUK/QMjLkTHp0QB2bxCIpgThXSNFDYNRxf1/aUqTfxQ8a+i0q7l/utRiWyY3p +kLOFx0n81a2bSNz+B0nPS338xNaBtuNjHedXjxN6BjUCUfFHFsQmg/y6ZbBRN/9P ++i2yy3CjLduh0yeD0Of3vAoIh19MuNgnIy1pMsK/H59E8bAaCFvND9fnsqAfH29W +ppmt9c8QaiAya5R7akl/C0Rx8khQrba+wMcSultqLU+6YVIAAEKZ/S/ZfN74wn5X +RXwSXp4MSmOKYqRWshvRegQ= + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName + urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName + urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos + urn:oasis:names:tc:SAML:2.0:nameid-format:entity + + + AWS Management Console Single Sign-On + + + + + + + + + + + + + + + + + + + + + + + Amazon Web Services, Inc. + AWS + https://aws.amazon.com + + + diff --git a/build/cloudid/root/opt/yunion/share/saml/sp-metadata/aws_cn.xml b/build/cloudid/root/opt/yunion/share/saml/sp-metadata/aws_cn.xml new file mode 100644 index 0000000000..057aa2a644 --- /dev/null +++ b/build/cloudid/root/opt/yunion/share/saml/sp-metadata/aws_cn.xml @@ -0,0 +1,67 @@ + + + + + + + MIIDfzCCAmegAwIBAgIEY1tz5jANBgkqhkiG9w0BAQsFADBwMSgwJgYDVQQDEx91 +cm46YW1hem9uOndlYnNlcnZpY2VzOmNuLW5vcnRoMSIwIAYDVQQKExlBbWF6b24g +V2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQIEwpXYXNoaW5ndG9uMQswCQYDVQQG +EwJVUzAeFw0yMDA1MTMwMDAwMDBaFw0yMTA1MTMwMDAwMDBaMHAxKDAmBgNVBAMT +H3VybjphbWF6b246d2Vic2VydmljZXM6Y24tbm9ydGgxIjAgBgNVBAoTGUFtYXpv +biBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAgTCldhc2hpbmd0b24xCzAJBgNV +BAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm2HLuzEnEGVT +C5W215HWuwFLM54v3oHoNy52GGN79VLn7sxm2Z0K+AVijV/rxCbPaNxO6tm/MmN6 +XJkU8g8p7ghT6p+EN/61WG+KgoXIHcl/aZBbkoNTKKPXRvR9EEcyAPdLDBJVJHF5 +lgCkSMHBL3VcCcrpVxFCtUhW+vT3pie2KHLEVb5Ocpq9pO8dNvY2iM7TSIvRu0kL +IZWUbRF0bP4cpDIVoMEsSJ3gFBCD3yf53UfU2dxjp2mnMjphPucHwml3AcsXN3XT +Y5j2qksO5xXgFrCZz76l3P7emj5eHIvkxuieU/7u+cYu0HppP+4xszzqvUcoHFRA +1fC/eFFRAwIDAQABoyEwHzAdBgNVHQ4EFgQUZkQfgy0NWq7kAO8mTQPeSDayJ3Ew +DQYJKoZIhvcNAQELBQADggEBACmLLD2qBA+KCxv7CiCfznT8XWOLAVf9xP3YMk0N +WCnugpd30d0YzxtI2PA29cNB2pyuGbaVg+zGqpsfAznaBHvnnEx1fJHNyfXxhAry +0MdabmbvhMfJPUYRFx/7r6Km8d52e+nv28pP/WrbWs4I0Swm+Rwaj3t32EgCBxQS +8WWEkG+L00slRgSPkfY8OgOM4QkBThHjGgw0TKFTEvQNVqXOJKQREq0MejyarUSC +4am+OJeRcWKtPcRo1jUlGWG6eun0ybuw232ZEZHUv33B247aRMuvTgcRTz3s6tUE +7RoeaMpl3/Y1MKtB2qURiu0Cib27Rl1mckhmfdA+awl3s4s= + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName + urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName + urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos + urn:oasis:names:tc:SAML:2.0:nameid-format:entity + + + AWS Management Console Single Sign-On + + + + + + + + + + + + + + + + + + + + + + + Amazon Web Services, Inc. + AWS + https://aws.amazon.com + + + diff --git a/build/cloudid/root/opt/yunion/share/saml/sp-metadata/gcp.xml b/build/cloudid/root/opt/yunion/share/saml/sp-metadata/gcp.xml new file mode 100644 index 0000000000..8f0b7fba49 --- /dev/null +++ b/build/cloudid/root/opt/yunion/share/saml/sp-metadata/gcp.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName + urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName + urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos + urn:oasis:names:tc:SAML:2.0:nameid-format:entity + + + + + + diff --git a/build/cloudid/root/opt/yunion/share/saml/sp-metadata/gcp_domain.xml b/build/cloudid/root/opt/yunion/share/saml/sp-metadata/gcp_domain.xml new file mode 100644 index 0000000000..2c79e34b34 --- /dev/null +++ b/build/cloudid/root/opt/yunion/share/saml/sp-metadata/gcp_domain.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName + urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName + urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos + urn:oasis:names:tc:SAML:2.0:nameid-format:entity + + + + + + diff --git a/build/cloudid/root/opt/yunion/share/saml/sp-metadata/huawei.xml b/build/cloudid/root/opt/yunion/share/saml/sp-metadata/huawei.xml new file mode 100644 index 0000000000..1f32645524 --- /dev/null +++ b/build/cloudid/root/opt/yunion/share/saml/sp-metadata/huawei.xml @@ -0,0 +1,79 @@ +diBMwyuN633Q/kBf0M+SQZ4fNCI=k6+6QX8T5+S5wEIWlxwvd5a48xLWmp4Jd0bcs33vNaQVQ93YiztGcroUUqnLYK8uqAsmk2ZP4NtKFL/MyqoznrStglSi7uycwX6X3fswMwvkHJ9UVhH6Gp2Txl2JQ9/0vXy/tlXjQNFbfqzLhqxKC/K/PmKd684XkPYXlkiEJa6nGibu0gse9rvP2hE0G9bDMfdiyQnanfBvN+oNRte3xyI3DWh2P/jDTAPJMYzGM76JIneS8jLPa4gKDz5KcumG/8JV7GUWTDTZZ4ftujsjEuUFdPzKqwMYogMXOnTt8wbisSF8ZlLSMH/TIj6xAO9aSkEAo8HtlHJbekYlVA5Tz5IamLLzfaPpf7+NghLSuATIwY8/pvBZ8qhY8PVOjzRCeoEZJUrlFOcZ2CvO9zVKYdkEa1JC3mUW7CgeQc7G2/9niub7Vu00eyp9AAd9nkPfLoiWam8/yg2TBPRRJ9VKsv2UMFuITWrcFJayjezlTzY3dI3tI8lfoIMEVRaEP1v1D8XbxnxiaiKZXsGQHtpTSLc1ZL441jeZDLa661raUJDlGA6mBc1QukJklEocGg+Q+FeU33MJCaN/rIZCGvrjIZNng3yKFw1+R7/CqeJJlFWw0hPJQGiy6wfrDYSmhwuXJ2vQn3dTHjlT3kWsniiZtuOUi2TjHbq8gVHqlxmPxSs=MIIF6TCCA9GgAwIBAgIEPHSCijANBgkqhkiG9w0BAQsFADCBpDELMAkGA1UEBhMCQ04xEjAQBgNV +BAgTCUd1YW5nRG9uZzERMA8GA1UEBxMIU2hlblpoZW4xJTAjBgNVBAoTHEh1YXdlaSBUZWNobm9s +b2dpZXMgQ28uLCBMdGQxKDAmBgNVBAsTH1NlcnZpY2UgUHJvdmlkZXIgT3BlcmF0aW9uIERlcHQx +HTAbBgNVBAMTFGF1dGguaHVhd2VpY2xvdWQuY29tMB4XDTE4MDYyMTEzMjUwMFoXDTI4MDYxODEz +MjUwMFowgaQxCzAJBgNVBAYTAkNOMRIwEAYDVQQIEwlHdWFuZ0RvbmcxETAPBgNVBAcTCFNoZW5a +aGVuMSUwIwYDVQQKExxIdWF3ZWkgVGVjaG5vbG9naWVzIENvLiwgTHRkMSgwJgYDVQQLEx9TZXJ2 +aWNlIFByb3ZpZGVyIE9wZXJhdGlvbiBEZXB0MR0wGwYDVQQDExRhdXRoLmh1YXdlaWNsb3VkLmNv +bTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJisHObeLOPs2mJ4eJBcruMZl9FjvRQe +DmofxgOcIcmybt6qlDqAv7275JMMQfQFcEoxH3GqcCTYqvSaSnHPaJB1xljPIKAWtd7p1ymevcSy +F2HdCZ8gPJKg3Q6+ZjwTipKS/nZr7xmpUQ0WvwRkgZ7zfslAW+y5PCgkDrgmEjG92rLArj8iPhmu +jajXaTPQKVHuZMxBzS735uo4yjVwYE3+0mE4HTZjK+6n6Ffu+JhLzhcKGulmrT/6qHisMbIXAZyE +egBDavomb+5zu/CUQwii5IAPrRTwwegYpG4+uYJ2cHfrUdHqw9lSCSbQzu1yW1AS4zB16sjoHZdV +rxYyktlswNmJ1/MyRH5bO90e2kvVwV4l34Hi5HEFvFFjL8TAsbN4mGvA9fgohXp30x97UdPVV8Ji +NNAKZjdZSEdG9xqrTRfe4+LQg/hzLNSwsko3nDnH8qhCgtb8qIipQ3s7niCa53AQWYR82lEViols +/dbWU9qYeldVvGNAgJSqHLB7qLwcQW78+2V1446KhQqzqPeLI4ANGaLFKw8fGzgh85RKOjrIetb4 +wAOZmhrrUJrRg47DYQQjNv3glDg53ijLPFunzRqUoqLphrZ1XpEA4y21OtTP6OMYAM0lSOj1gjvb +ubTDo8XOQs5YGtTOyHn4CQ4GR8NNo7UrwVEmoZHA+AKbAgMBAAGjITAfMB0GA1UdDgQWBBTt05QW +9dyXi7eMekKOH0bn4xKrkDANBgkqhkiG9w0BAQsFAAOCAgEACv9zgzUgxxQ8t9ldOXmirxzSOrHx +MCL8SKsu+c+Y4hoHma5LFjylv6x76NWTAFSE6GgqfuNI/gPj/2AWqObAvsHd8lsPjJ96ZoSaTmS8 +NrtU6HuT1Lc+CmVfeGd3/G+KspQECjg2JeBrfyEw9B8KUAQV20DQukGfAHtKKQPOZmKm0Qm3ExWC +eXz1TR2KP+Lrhny/yG43g4iVUKq65HFHs5cRzRk0iR0/NLpggl5+Op0rxMxBn+bCrnJBi0n9/PIM +fWNhkEBl+B++EifPUQxQaOEsnxTgFo1O4ksK9hDFcLbr+1qCDgVIMkyC1xMBBikCgLvIdzy3SXBN +ndIEUq+QgOORxLlq1WqrfLFO22TxZm8XaB5g36UMk5PGoVHGnjALReAHjC0C5sIiKMJSgQOPd71X +mQSsw3G9NsMKf/H3xJXkq/b672ls/l1JBslm52DAk2k5UlLkf/1p4I7WHOfm5ZNpDjj1rTP6SiAc +tWLtqXIU28fLa2sA+zHXA5acDGOm6eIrMme5HpsV/KoUOW1MXGugK59zofeueCFDGRfbyoS2lj0S +W+CbJVa72CLf3xPh2nWH0cK9de+wyCx8uI0KGPyV4I9/XBLHhvkb3XPaUfnkzYkcrG/39cOaxuPF +z+haXwI1lvI964zvmTgwdDjdf/0asA09S7EEK2KyzXUREM4=MIIF6TCCA9GgAwIBAgIEPHSCijANBgkqhkiG9w0BAQsFADCBpDELMAkGA1UEBhMCQ04xEjAQBgNV +BAgTCUd1YW5nRG9uZzERMA8GA1UEBxMIU2hlblpoZW4xJTAjBgNVBAoTHEh1YXdlaSBUZWNobm9s +b2dpZXMgQ28uLCBMdGQxKDAmBgNVBAsTH1NlcnZpY2UgUHJvdmlkZXIgT3BlcmF0aW9uIERlcHQx +HTAbBgNVBAMTFGF1dGguaHVhd2VpY2xvdWQuY29tMB4XDTE4MDYyMTEzMjUwMFoXDTI4MDYxODEz +MjUwMFowgaQxCzAJBgNVBAYTAkNOMRIwEAYDVQQIEwlHdWFuZ0RvbmcxETAPBgNVBAcTCFNoZW5a +aGVuMSUwIwYDVQQKExxIdWF3ZWkgVGVjaG5vbG9naWVzIENvLiwgTHRkMSgwJgYDVQQLEx9TZXJ2 +aWNlIFByb3ZpZGVyIE9wZXJhdGlvbiBEZXB0MR0wGwYDVQQDExRhdXRoLmh1YXdlaWNsb3VkLmNv +bTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJisHObeLOPs2mJ4eJBcruMZl9FjvRQe +DmofxgOcIcmybt6qlDqAv7275JMMQfQFcEoxH3GqcCTYqvSaSnHPaJB1xljPIKAWtd7p1ymevcSy +F2HdCZ8gPJKg3Q6+ZjwTipKS/nZr7xmpUQ0WvwRkgZ7zfslAW+y5PCgkDrgmEjG92rLArj8iPhmu +jajXaTPQKVHuZMxBzS735uo4yjVwYE3+0mE4HTZjK+6n6Ffu+JhLzhcKGulmrT/6qHisMbIXAZyE +egBDavomb+5zu/CUQwii5IAPrRTwwegYpG4+uYJ2cHfrUdHqw9lSCSbQzu1yW1AS4zB16sjoHZdV +rxYyktlswNmJ1/MyRH5bO90e2kvVwV4l34Hi5HEFvFFjL8TAsbN4mGvA9fgohXp30x97UdPVV8Ji +NNAKZjdZSEdG9xqrTRfe4+LQg/hzLNSwsko3nDnH8qhCgtb8qIipQ3s7niCa53AQWYR82lEViols +/dbWU9qYeldVvGNAgJSqHLB7qLwcQW78+2V1446KhQqzqPeLI4ANGaLFKw8fGzgh85RKOjrIetb4 +wAOZmhrrUJrRg47DYQQjNv3glDg53ijLPFunzRqUoqLphrZ1XpEA4y21OtTP6OMYAM0lSOj1gjvb +ubTDo8XOQs5YGtTOyHn4CQ4GR8NNo7UrwVEmoZHA+AKbAgMBAAGjITAfMB0GA1UdDgQWBBTt05QW +9dyXi7eMekKOH0bn4xKrkDANBgkqhkiG9w0BAQsFAAOCAgEACv9zgzUgxxQ8t9ldOXmirxzSOrHx +MCL8SKsu+c+Y4hoHma5LFjylv6x76NWTAFSE6GgqfuNI/gPj/2AWqObAvsHd8lsPjJ96ZoSaTmS8 +NrtU6HuT1Lc+CmVfeGd3/G+KspQECjg2JeBrfyEw9B8KUAQV20DQukGfAHtKKQPOZmKm0Qm3ExWC +eXz1TR2KP+Lrhny/yG43g4iVUKq65HFHs5cRzRk0iR0/NLpggl5+Op0rxMxBn+bCrnJBi0n9/PIM +fWNhkEBl+B++EifPUQxQaOEsnxTgFo1O4ksK9hDFcLbr+1qCDgVIMkyC1xMBBikCgLvIdzy3SXBN +ndIEUq+QgOORxLlq1WqrfLFO22TxZm8XaB5g36UMk5PGoVHGnjALReAHjC0C5sIiKMJSgQOPd71X +mQSsw3G9NsMKf/H3xJXkq/b672ls/l1JBslm52DAk2k5UlLkf/1p4I7WHOfm5ZNpDjj1rTP6SiAc +tWLtqXIU28fLa2sA+zHXA5acDGOm6eIrMme5HpsV/KoUOW1MXGugK59zofeueCFDGRfbyoS2lj0S +W+CbJVa72CLf3xPh2nWH0cK9de+wyCx8uI0KGPyV4I9/XBLHhvkb3XPaUfnkzYkcrG/39cOaxuPF +z+haXwI1lvI964zvmTgwdDjdf/0asA09S7EEK2KyzXUREM4=MIIF6TCCA9GgAwIBAgIEPHSCijANBgkqhkiG9w0BAQsFADCBpDELMAkGA1UEBhMCQ04xEjAQBgNV +BAgTCUd1YW5nRG9uZzERMA8GA1UEBxMIU2hlblpoZW4xJTAjBgNVBAoTHEh1YXdlaSBUZWNobm9s +b2dpZXMgQ28uLCBMdGQxKDAmBgNVBAsTH1NlcnZpY2UgUHJvdmlkZXIgT3BlcmF0aW9uIERlcHQx +HTAbBgNVBAMTFGF1dGguaHVhd2VpY2xvdWQuY29tMB4XDTE4MDYyMTEzMjUwMFoXDTI4MDYxODEz +MjUwMFowgaQxCzAJBgNVBAYTAkNOMRIwEAYDVQQIEwlHdWFuZ0RvbmcxETAPBgNVBAcTCFNoZW5a +aGVuMSUwIwYDVQQKExxIdWF3ZWkgVGVjaG5vbG9naWVzIENvLiwgTHRkMSgwJgYDVQQLEx9TZXJ2 +aWNlIFByb3ZpZGVyIE9wZXJhdGlvbiBEZXB0MR0wGwYDVQQDExRhdXRoLmh1YXdlaWNsb3VkLmNv +bTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJisHObeLOPs2mJ4eJBcruMZl9FjvRQe +DmofxgOcIcmybt6qlDqAv7275JMMQfQFcEoxH3GqcCTYqvSaSnHPaJB1xljPIKAWtd7p1ymevcSy +F2HdCZ8gPJKg3Q6+ZjwTipKS/nZr7xmpUQ0WvwRkgZ7zfslAW+y5PCgkDrgmEjG92rLArj8iPhmu +jajXaTPQKVHuZMxBzS735uo4yjVwYE3+0mE4HTZjK+6n6Ffu+JhLzhcKGulmrT/6qHisMbIXAZyE +egBDavomb+5zu/CUQwii5IAPrRTwwegYpG4+uYJ2cHfrUdHqw9lSCSbQzu1yW1AS4zB16sjoHZdV +rxYyktlswNmJ1/MyRH5bO90e2kvVwV4l34Hi5HEFvFFjL8TAsbN4mGvA9fgohXp30x97UdPVV8Ji +NNAKZjdZSEdG9xqrTRfe4+LQg/hzLNSwsko3nDnH8qhCgtb8qIipQ3s7niCa53AQWYR82lEViols +/dbWU9qYeldVvGNAgJSqHLB7qLwcQW78+2V1446KhQqzqPeLI4ANGaLFKw8fGzgh85RKOjrIetb4 +wAOZmhrrUJrRg47DYQQjNv3glDg53ijLPFunzRqUoqLphrZ1XpEA4y21OtTP6OMYAM0lSOj1gjvb +ubTDo8XOQs5YGtTOyHn4CQ4GR8NNo7UrwVEmoZHA+AKbAgMBAAGjITAfMB0GA1UdDgQWBBTt05QW +9dyXi7eMekKOH0bn4xKrkDANBgkqhkiG9w0BAQsFAAOCAgEACv9zgzUgxxQ8t9ldOXmirxzSOrHx +MCL8SKsu+c+Y4hoHma5LFjylv6x76NWTAFSE6GgqfuNI/gPj/2AWqObAvsHd8lsPjJ96ZoSaTmS8 +NrtU6HuT1Lc+CmVfeGd3/G+KspQECjg2JeBrfyEw9B8KUAQV20DQukGfAHtKKQPOZmKm0Qm3ExWC +eXz1TR2KP+Lrhny/yG43g4iVUKq65HFHs5cRzRk0iR0/NLpggl5+Op0rxMxBn+bCrnJBi0n9/PIM +fWNhkEBl+B++EifPUQxQaOEsnxTgFo1O4ksK9hDFcLbr+1qCDgVIMkyC1xMBBikCgLvIdzy3SXBN +ndIEUq+QgOORxLlq1WqrfLFO22TxZm8XaB5g36UMk5PGoVHGnjALReAHjC0C5sIiKMJSgQOPd71X +mQSsw3G9NsMKf/H3xJXkq/b672ls/l1JBslm52DAk2k5UlLkf/1p4I7WHOfm5ZNpDjj1rTP6SiAc +tWLtqXIU28fLa2sA+zHXA5acDGOm6eIrMme5HpsV/KoUOW1MXGugK59zofeueCFDGRfbyoS2lj0S +W+CbJVa72CLf3xPh2nWH0cK9de+wyCx8uI0KGPyV4I9/XBLHhvkb3XPaUfnkzYkcrG/39cOaxuPF +z+haXwI1lvI964zvmTgwdDjdf/0asA09S7EEK2KyzXUREM4=urn:oasis:names:tc:SAML:2.0:nameid-format:transient \ No newline at end of file diff --git a/build/cloudid/root/opt/yunion/share/saml/sp-metadata/qcloud.xml b/build/cloudid/root/opt/yunion/share/saml/sp-metadata/qcloud.xml new file mode 100644 index 0000000000..07c0799c5e --- /dev/null +++ b/build/cloudid/root/opt/yunion/share/saml/sp-metadata/qcloud.xml @@ -0,0 +1,28 @@ + + + + + + + MIIEWzCCA0OgAwIBAgIJAP2Wa/iz6Zn+MA0GCSqGSIb3DQEBBQUAMIHDMQswCQYDVQQGEwJDTjESMBAGA1UECAwJZ3Vhbmdkb25nMREwDwYDVQQHDAhzaGVuemhlbjE2MDQGA1UECgwtVGVuY2VudCBUZWNobm9sb2d5IChTaGVuemhlbikgQ29tcGFueSBMaW1pdGVkMRYwFAYDVQQLDA1UZW5jZW50IENsb3VkMRowGAYDVQQDDBFjbG91ZC50ZW5jZW50LmNvbTEhMB8GCSqGSIb3DQEJARYScWNsb3VkQHRlbmNlbnQuY29tMB4XDTE4MDgxNTA2MjQzMVoXDTE5MDgxNTA2MjQzMVowgcMxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlndWFuZ2RvbmcxETAPBgNVBAcMCHNoZW56aGVuMTYwNAYDVQQKDC1UZW5jZW50IFRlY2hub2xvZ3kgKFNoZW56aGVuKSBDb21wYW55IExpbWl0ZWQxFjAUBgNVBAsMDVRlbmNlbnQgQ2xvdWQxGjAYBgNVBAMMEWNsb3VkLnRlbmNlbnQuY29tMSEwHwYJKoZIhvcNAQkBFhJxY2xvdWRAdGVuY2VudC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7z2KMtR6GS4IzwfFYLSULCrKrr6iUVRlxUhn0y/CjpWXGg31nwvXYmJZaCwjUlorTd3pYDmStmd4xoL3xrGFnqX0xqKw7S+vbB9wo5tSHww15vkiYPanjsgeaiXihBBB6jvsV8+4iuVk8JA3x990/0wic/ZuKIhCI4mLCaQYLXbsYopUPQ5p+dwkCY9pYxP+zjOQathA0cjzKyzp9io2pcH2vtj7RzM+j4sLTsTC+OGfh7k9LQO5/fswQTQKr1B4tmtI8Cq7pUP0Fud1YLjMpod/NRVv1/xIF6cJFP8QARfmC45FPjHToORiXVUV8hii8l4EkXw2bAQO+wqodXJaZAgMBAAGjUDBOMB0GA1UdDgQWBBQ6e8nFUT7oKF+LbOYFH200WR5I0zAfBgNVHSMEGDAWgBQ6e8nFUT7oKF+LbOYFH200WR5I0zAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQBAIaZDjgCLkWq6fFpp/ViwdEBtLbKNNx2K28z91kTZNE+lepYagqWQIPpTGo69GmO9Qdf/mkspY3Y80Q3MpUO09V3gsiqXQBHD5ZfyRHwmqDjDudLA1SGzMjMT+48XuMq4fdonO1o5fyDh/My9w1LuRZzWw5ZE0JEpKgXq1ynQYz0uEip7KrUVUNoAS2n9jT8APfs16M4p5VRpIYlugnBwR9P2bZba+9fR/SPTa7FX4S8sU649wGPR+49uvic2kwc8FP6AXGurJZhd7mZ44j58q0M/LXm/6wiyUslZ/I0uGhxmRav0cTQz4T1dXKdh2pJZBwz8yMv2P6fuxcFlUeR6 + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + urn:oasis:names:tc:SAML:2.0:nameid-format:entity + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName + + + + + + + + + Tencent Cloud Services, Inc. + Tencent Cloud + https://cloud.tencent.com + + diff --git a/build/docker/Dockerfile.cloudid b/build/docker/Dockerfile.cloudid index 4e9a683e60..2ffcf573bf 100644 --- a/build/docker/Dockerfile.cloudid +++ b/build/docker/Dockerfile.cloudid @@ -1,3 +1,4 @@ FROM registry.cn-beijing.aliyuncs.com/yunionio/onecloud-base:v0.1 +COPY ./build/cloudid/root/opt/ /opt/ ADD ./_output/bin/cloudid /opt/yunion/bin/cloudid diff --git a/cmd/aliyuncli/main.go b/cmd/aliyuncli/main.go index 53344fe3ad..a26b4cabd1 100644 --- a/cmd/aliyuncli/main.go +++ b/cmd/aliyuncli/main.go @@ -28,9 +28,9 @@ import ( type BaseOptions struct { Debug bool `help:"debug mode"` Help bool `help:"Show help"` - AccessKey string `help:"Access key" default:"$ALIYUN_ACCESS_KEY"` - Secret string `help:"Secret" default:"$ALIYUN_SECRET"` - RegionId string `help:"RegionId" default:"$ALIYUN_REGION"` + AccessKey string `help:"Access key" default:"$ALIYUN_ACCESS_KEY" metavar:"ALIYUN_ACCESS_KEY"` + Secret string `help:"Secret" default:"$ALIYUN_SECRET" metavar:"ALIYUN_SECRET"` + RegionId string `help:"RegionId" default:"$ALIYUN_REGION" metavar:"ALIYUN_REGION"` SUBCOMMAND string `help:"aliyuncli subcommand" subcommand:"true"` } diff --git a/cmd/awscli/main.go b/cmd/awscli/main.go index 1217f56dd8..e0f568b696 100644 --- a/cmd/awscli/main.go +++ b/cmd/awscli/main.go @@ -28,10 +28,10 @@ import ( type BaseOptions struct { Help bool `help:"Show help"` Debug bool `help:"debug mode"` - AccessUrl string `help:"Access key" default:"$AWS_ACCESS_URL" choices:"ChinaCloud|InternationalCloud"` - AccessKey string `help:"Access key" default:"$AWS_ACCESS_KEY"` - Secret string `help:"Secret" default:"$AWS_SECRET"` - RegionId string `help:"RegionId" default:"$AWS_REGION"` + AccessUrl string `help:"Access key" default:"$AWS_ACCESS_URL" choices:"ChinaCloud|InternationalCloud" metavar:"AWS_ACCESS_URL"` + AccessKey string `help:"Access key" default:"$AWS_ACCESS_KEY" metavar:"AWS_ACCESS_KEY"` + Secret string `help:"Secret" default:"$AWS_SECRET" metavar:"AWS_SECRET"` + RegionId string `help:"RegionId" default:"$AWS_REGION" metavar:"AWS_REGION"` SUBCOMMAND string `help:"awscli subcommand" subcommand:"true"` } diff --git a/cmd/azurecli/main.go b/cmd/azurecli/main.go index 4c9f4a3dad..3f338d3436 100644 --- a/cmd/azurecli/main.go +++ b/cmd/azurecli/main.go @@ -29,12 +29,12 @@ import ( type BaseOptions struct { Help bool `help:"Show help"` Debug bool `help:"debug mode"` - DirectoryID string `help:"Azure account Directory ID/Tenant ID" default:"$AZURE_DIRECTORY_ID"` - SubscriptionID string `help:"Azure account subscription ID" default:"$AZURE_SUBSCRIPTION_ID"` - ApplicationID string `help:"Azure application ID" default:"$AZURE_APPLICATION_ID"` - ApplicationKey string `help:"Azure application key" default:"$AZURE_APPLICATION_KEY"` - RegionId string `help:"RegionId" default:"$AZURE_REGION_ID"` - CloudEnv string `help:"Cloud Environment" default:"$AZURE_CLOUD_ENV" choices:"AzureGermanCloud|AzureChinaCloud|AzureUSGovernmentCloud|AzurePublicCloud"` + DirectoryID string `help:"Azure account Directory ID/Tenant ID" default:"$AZURE_DIRECTORY_ID" metavar:"AZURE_DIRECTORY_ID"` + SubscriptionID string `help:"Azure account subscription ID" default:"$AZURE_SUBSCRIPTION_ID" metavar:"AZURE_SUBSCRIPTION_ID"` + ApplicationID string `help:"Azure application ID" default:"$AZURE_APPLICATION_ID" metavar:"AZURE_APPLICATION_ID"` + ApplicationKey string `help:"Azure application key" default:"$AZURE_APPLICATION_KEY" metavar:"AZURE_APPLICATION_KEY"` + RegionId string `help:"RegionId" default:"$AZURE_REGION_ID" metavar:"AZURE_REGION_ID"` + CloudEnv string `help:"Cloud Environment" default:"$AZURE_CLOUD_ENV" choices:"AzureGermanCloud|AzureChinaCloud|AzureUSGovernmentCloud|AzurePublicCloud" metavar:"AZURE_CLOUD_ENV"` SUBCOMMAND string `help:"azurecli subcommand" subcommand:"true"` } diff --git a/cmd/climc/shell/compute/cloudaccounts.go b/cmd/climc/shell/compute/cloudaccounts.go index 4cb79e7fd3..56ecdb58d4 100644 --- a/cmd/climc/shell/compute/cloudaccounts.go +++ b/cmd/climc/shell/compute/cloudaccounts.go @@ -1168,4 +1168,12 @@ func init() { return nil }) + R(&CloudaccountShowOptions{}, "cloud-account-saml", "Get saml info details of a cloud account", func(s *mcclient.ClientSession, args *CloudaccountShowOptions) error { + result, err := modules.Cloudaccounts.GetSpecific(s, args.ID, "saml", nil) + if err != nil { + return err + } + printObject(result) + return nil + }) } diff --git a/cmd/esxicli/main.go b/cmd/esxicli/main.go index 7114b230a5..1296370f8c 100644 --- a/cmd/esxicli/main.go +++ b/cmd/esxicli/main.go @@ -27,10 +27,10 @@ import ( type BaseOptions struct { Help bool `help:"Show help"` - Host string `help:"Host IP or NAME" default:"$VMWARE_HOST"` - Port int `help:"Service port" default:"$VMWARE_PORT"` - Account string `help:"VCenter or ESXi Account" default:"$VMWARE_ACCOUNT"` - Password string `help:"Password" default:"$VMWARE_PASSWORD"` + Host string `help:"Host IP or NAME" default:"$VMWARE_HOST" metavar:"VMWARE_HOST"` + Port int `help:"Service port" default:"$VMWARE_PORT" metavar:"VMWARE_PORT"` + Account string `help:"VCenter or ESXi Account" default:"$VMWARE_ACCOUNT" metavar:"VMWARE_ACCOUNT"` + Password string `help:"Password" default:"$VMWARE_PASSWORD" metavar:"VMWARE_PASSWORD"` SUBCOMMAND string `help:"aliyuncli subcommand" subcommand:"true"` } diff --git a/cmd/googlecli/main.go b/cmd/googlecli/main.go index 6b8ab5965b..926e7da9ab 100644 --- a/cmd/googlecli/main.go +++ b/cmd/googlecli/main.go @@ -32,12 +32,12 @@ import ( type BaseOptions struct { Debug bool `help:"debug mode"` Help bool `help:"Show help"` - AuthFile string `help:"google cloud auth json file path" default:"$GOOGLE_AUTH_FILE"` - ClientEmail string `help:"Client email" default:"$GOOGLE_CLIENT_EMAIL"` - ProjectID string `help:"Project ID" default:"$GOOGLE_PROJECT_ID"` - PrivateKeyID string `help:"Private Key ID" default:"$GOOGLE_PRIVATE_KEY_ID"` - PrivateKey string `help:"Private Key" default:"$GOOGLE_PRIVATE_KEY"` - RegionID string `help:"RegionID" default:"$GOOGLE_REGION"` + AuthFile string `help:"google cloud auth json file path" default:"$GOOGLE_AUTH_FILE" metavar:"GOOGLE_AUTH_FILE"` + ClientEmail string `help:"Client email" default:"$GOOGLE_CLIENT_EMAIL" metavar:"GOOGLE_CLIENT_EMAIL"` + ProjectID string `help:"Project ID" default:"$GOOGLE_PROJECT_ID" metavar:"GOOGLE_PROJECT_ID"` + PrivateKeyID string `help:"Private Key ID" default:"$GOOGLE_PRIVATE_KEY_ID" metavar:"GOOGLE_PRIVATE_KEY_ID"` + PrivateKey string `help:"Private Key" default:"$GOOGLE_PRIVATE_KEY" metavar:"GOOGLE_PRIVATE_KEY"` + RegionID string `help:"RegionID" default:"$GOOGLE_REGION" metavar:"GOOGLE_REGION"` SUBCOMMAND string `help:"googlecli subcommand" subcommand:"true"` } diff --git a/cmd/huaweicli/main.go b/cmd/huaweicli/main.go index ba0778920f..a55826181c 100644 --- a/cmd/huaweicli/main.go +++ b/cmd/huaweicli/main.go @@ -28,11 +28,11 @@ import ( type BaseOptions struct { Help bool `help:"Show help" default:"false"` Debug bool `help:"Show debug" default:"false"` - CloudEnv string `help:"Cloud environment" default:"$HUAWEI_CLOUD_ENV" choices:"ChinaCloud|InternationalCloud"` - AccessKey string `help:"Access key" default:"$HUAWEI_ACCESS_KEY"` - Secret string `help:"Secret" default:"$HUAWEI_SECRET"` - RegionId string `help:"RegionId" default:"$HUAWEI_REGION"` - ProjectId string `help:"RegionId" default:"$HUAWEI_PROJECT"` + CloudEnv string `help:"Cloud environment" default:"$HUAWEI_CLOUD_ENV" choices:"ChinaCloud|InternationalCloud" metavar:"HUAWEI_CLOUD_ENV"` + AccessKey string `help:"Access key" default:"$HUAWEI_ACCESS_KEY" metavar:"HUAWEI_ACCESS_KEY"` + Secret string `help:"Secret" default:"$HUAWEI_SECRET" metavar:"HUAWEI_SECRET"` + RegionId string `help:"RegionId" default:"$HUAWEI_REGION" metavar:"HUAWEI_REGION"` + ProjectId string `help:"RegionId" default:"$HUAWEI_PROJECT" metavar:"HUAWEI_PROJECT"` SUBCOMMAND string `help:"huaweicli subcommand" subcommand:"true"` } diff --git a/cmd/openstackcli/main.go b/cmd/openstackcli/main.go index 0b87dffc00..8560fd4bea 100644 --- a/cmd/openstackcli/main.go +++ b/cmd/openstackcli/main.go @@ -28,14 +28,14 @@ import ( type BaseOptions struct { Debug bool `help:"debug mode"` Help bool `help:"Show help"` - AuthURL string `help:"Auth URL" default:"$OPENSTACK_AUTH_URL"` - Username string `help:"Username" default:"$OPENSTACK_USERNAME"` - Password string `help:"Password" default:"$OPENSTACK_PASSWORD"` - Project string `help:"Project" default:"$OPENSTACK_PROJECT"` - EndpointType string `help:"Project" default:"$OPENSTACK_ENDPOINT_TYPE|internal"` - DomainName string `help:"Domain of user" default:"$OPENSTACK_DOMAIN_NAME|Default"` - ProjectDomain string `help:"Domain of project" default:"$OPENSTACK_PROJECT_DOMAIN|Default"` - RegionID string `help:"RegionId" default:"$OPENSTACK_REGION_ID"` + AuthURL string `help:"Auth URL" default:"$OPENSTACK_AUTH_URL" metavar:"OPENSTACK_AUTH_URL"` + Username string `help:"Username" default:"$OPENSTACK_USERNAME" metavar:"OPENSTACK_USERNAME"` + Password string `help:"Password" default:"$OPENSTACK_PASSWORD" metavar:"OPENSTACK_PASSWORD"` + Project string `help:"Project" default:"$OPENSTACK_PROJECT" metavar:"OPENSTACK_PROJECT"` + EndpointType string `help:"Project" default:"$OPENSTACK_ENDPOINT_TYPE|internal" metavar:"OPENSTACK_ENDPOINT_TYPE"` + DomainName string `help:"Domain of user" default:"$OPENSTACK_DOMAIN_NAME|Default" metavar:"OPENSTACK_DOMAIN_NAME"` + ProjectDomain string `help:"Domain of project" default:"$OPENSTACK_PROJECT_DOMAIN|Default" metavar:"OPENSTACK_PROJECT_DOMAIN"` + RegionID string `help:"RegionId" default:"$OPENSTACK_REGION_ID" metavar:"OPENSTACK_REGION_ID"` SUBCOMMAND string `help:"openstackcli subcommand" subcommand:"true"` } diff --git a/cmd/qcloudcli/main.go b/cmd/qcloudcli/main.go index f53db18a48..8de20de837 100644 --- a/cmd/qcloudcli/main.go +++ b/cmd/qcloudcli/main.go @@ -28,10 +28,10 @@ import ( type BaseOptions struct { Debug bool `help:"debug mode"` Help bool `help:"Show help"` - AppID string `help:"AppID" default:"$QCLOUD_APPID"` - SecretID string `help:"Secret" default:"$QCLOUD_SECRET_ID"` - SecretKey string `help:"Access key" default:"$QCLOUD_SECRET_KEY"` - RegionId string `help:"RegionId" default:"$QCLOUD_REGION"` + AppID string `help:"AppID" default:"$QCLOUD_APPID" metavar:"QCLOUD_APPID"` + SecretID string `help:"Secret" default:"$QCLOUD_SECRET_ID" metavar:"QCLOUD_SECRET_ID"` + SecretKey string `help:"Access key" default:"$QCLOUD_SECRET_KEY" metavar:"QCLOUD_SECRET_KEY"` + RegionId string `help:"RegionId" default:"$QCLOUD_REGION" metavar:"QCLOUD_REGION"` SUBCOMMAND string `help:"azurecli subcommand" subcommand:"true"` } diff --git a/cmd/redfishcli/main.go b/cmd/redfishcli/main.go index 15922a915b..4c685d3278 100644 --- a/cmd/redfishcli/main.go +++ b/cmd/redfishcli/main.go @@ -30,9 +30,9 @@ import ( type BaseOptions struct { Debug bool `help:"debug mode"` Help bool `help:"Show help"` - Endpoint string `help:"Endpoint, usually https://" default:"$REDFISH_ENDPOINT"` - Username string `help:"Username, usually root" default:"$REDFISH_USERNAME"` - Password string `help:"Password" default:"$REDFISH_PASSWORD"` + Endpoint string `help:"Endpoint, usually https://" default:"$REDFISH_ENDPOINT" metavar:"REDFISH_ENDPOINT"` + Username string `help:"Username, usually root" default:"$REDFISH_USERNAME" metavar:"REDFISH_USERNAME"` + Password string `help:"Password" default:"$REDFISH_PASSWORD" metavar:"REDFISH_PASSWORD"` SUBCOMMAND string `help:"s3cli subcommand" subcommand:"true"` } diff --git a/cmd/s3cli/main.go b/cmd/s3cli/main.go index 2c6fee1903..5c99f9cc0b 100644 --- a/cmd/s3cli/main.go +++ b/cmd/s3cli/main.go @@ -32,10 +32,10 @@ import ( type BaseOptions struct { Debug bool `help:"debug mode"` Help bool `help:"Show help"` - AccessUrl string `help:"Access url" default:"$S3_ACCESS_URL"` - AccessKey string `help:"Access key" default:"$S3_ACCESS_KEY"` - Secret string `help:"Secret" default:"$S3_SECRET"` - Backend string `help:"Backend driver" default:"$S3_BACKEND"` + AccessUrl string `help:"Access url" default:"$S3_ACCESS_URL" metavar:"S3_ACCESS_URL"` + AccessKey string `help:"Access key" default:"$S3_ACCESS_KEY" metavar:"S3_ACCESS_KEY"` + Secret string `help:"Secret" default:"$S3_SECRET" metavar:"S3_SECRET"` + Backend string `help:"Backend driver" default:"$S3_BACKEND" metavar:"S3_BACKEND"` SUBCOMMAND string `help:"s3cli subcommand" subcommand:"true"` } diff --git a/cmd/ucloudcli/main.go b/cmd/ucloudcli/main.go index fd912e7e04..0b7d99cd67 100644 --- a/cmd/ucloudcli/main.go +++ b/cmd/ucloudcli/main.go @@ -29,10 +29,10 @@ type BaseOptions struct { Help bool `help:"Show help" default:"false"` Debug bool `help:"Show debug" default:"false"` - AccessKey string `help:"Access key" default:"$UCLOUD_ACCESS_KEY"` - Secret string `help:"Secret" default:"$UCLOUD_SECRET"` - RegionId string `help:"RegionId" default:"$UCLOUD_REGION"` - ProjectId string `help:"ProjectId" default:"$UCLOUD_PROJECT"` + AccessKey string `help:"Access key" default:"$UCLOUD_ACCESS_KEY" metavar:"UCLOUD_ACCESS_KEY"` + Secret string `help:"Secret" default:"$UCLOUD_SECRET" metavar:"UCLOUD_SECRET"` + RegionId string `help:"RegionId" default:"$UCLOUD_REGION" metavar:"UCLOUD_REGION"` + ProjectId string `help:"ProjectId" default:"$UCLOUD_PROJECT" metavar:"UCLOUD_PROJECT"` SUBCOMMAND string `help:"ucloudcli subcommand" subcommand:"true"` } diff --git a/cmd/zstackcli/main.go b/cmd/zstackcli/main.go index 4694b18b3a..00c8e96ba1 100644 --- a/cmd/zstackcli/main.go +++ b/cmd/zstackcli/main.go @@ -28,10 +28,10 @@ import ( type BaseOptions struct { Debug bool `help:"debug mode"` Help bool `help:"Show help"` - AuthURL string `help:"Auth URL" default:"$ZSTACK_AUTH_URL"` - Username string `help:"Username" default:"$ZSTACK_USERNAME"` - Password string `help:"Password" default:"$ZSTACK_PASSWORD"` - RegionID string `help:"RegionId" default:"$ZSTACK_REGION_ID"` + AuthURL string `help:"Auth URL" default:"$ZSTACK_AUTH_URL" metavar:"ZSTACK_AUTH_URL"` + Username string `help:"Username" default:"$ZSTACK_USERNAME" metavar:"ZSTACK_USERNAME"` + Password string `help:"Password" default:"$ZSTACK_PASSWORD" metavar:"ZSTACK_PASSWORD"` + RegionID string `help:"RegionId" default:"$ZSTACK_REGION_ID" metavar:"ZSTACK_REGION_ID"` SUBCOMMAND string `help:"zstackcli subcommand" subcommand:"true"` } diff --git a/pkg/apigateway/app/app.go b/pkg/apigateway/app/app.go index 6ae8d4700e..a3fc38ded3 100644 --- a/pkg/apigateway/app/app.go +++ b/pkg/apigateway/app/app.go @@ -16,6 +16,7 @@ package app import ( "yunion.io/x/onecloud/pkg/apigateway/handler" + "yunion.io/x/onecloud/pkg/apis/cloudid" "yunion.io/x/onecloud/pkg/appsrv" ) @@ -29,6 +30,7 @@ type Application struct { CSRFResourceHandler handler.IHandler RPCHandler handler.IHandler InfluxdbProxyHandler handler.IHandler + CloudIdSAMLHandler handler.IHandler } func NewApp(app *appsrv.Application) *Application { @@ -66,11 +68,14 @@ func (app *Application) InitHandlers() *Application { app.InfluxdbProxyHandler = handler.NewInfluxdbProxyHandler("/query") + app.CloudIdSAMLHandler = handler.NewProxyHandlerWithService(cloudid.SAML_IDP_PREFIX, cloudid.SERVICE_TYPE) + return app } func (app *Application) Bind() { for _, h := range []handler.IHandler{ + app.CloudIdSAMLHandler, app.InfluxdbProxyHandler, app.MiscHandler, app.AuthHandler, diff --git a/pkg/apigateway/constants/constants.go b/pkg/apigateway/constants/constants.go index bc79f5833e..bbc87a3572 100644 --- a/pkg/apigateway/constants/constants.go +++ b/pkg/apigateway/constants/constants.go @@ -17,7 +17,6 @@ package constants const ( AUTH_PREFIX = "Bearer " AUTH_HEADER = "Authorization" - AUTH_TOKEN = "AUTH_TOKEN" YUNION_AUTH_COOKIE = "yunionauth" REGION_COOKIE = "region" ) diff --git a/pkg/apigateway/handler/auth.go b/pkg/apigateway/handler/auth.go index 264d7cbb80..d23ed95421 100644 --- a/pkg/apigateway/handler/auth.go +++ b/pkg/apigateway/handler/auth.go @@ -31,7 +31,6 @@ import ( "yunion.io/x/onecloud/pkg/apigateway/options" policytool "yunion.io/x/onecloud/pkg/apigateway/policy" "yunion.io/x/onecloud/pkg/apis/compute" - "yunion.io/x/onecloud/pkg/appctx" "yunion.io/x/onecloud/pkg/appsrv" "yunion.io/x/onecloud/pkg/cloudcommon/policy" "yunion.io/x/onecloud/pkg/httperrors" @@ -46,11 +45,7 @@ import ( ) func AppContextToken(ctx context.Context) mcclient.TokenCredential { - val := ctx.Value(appctx.AppContextKey(constants.AUTH_TOKEN)) - if val == nil { - return nil - } - return val.(mcclient.TokenCredential) + return auth.FetchUserCredential(ctx, nil) } type AuthHandlers struct { @@ -72,6 +67,7 @@ func (h *AuthHandlers) AddMethods() { NewHP(h.getIdpSsoRedirectUri, "sso", "redirect", ""), NewHP(h.listTotpRecoveryQuestions, "recovery"), NewHP(h.handleSsoLogin, "ssologin"), + // oidc auth NewHP(handleOIDCAuth, "oidc", "auth"), NewHP(handleOIDCConfiguration, "oidc", ".well-known", "openid-configuration"), NewHP(handleOIDCJWKeys, "oidc", "keys"), @@ -95,6 +91,7 @@ func (h *AuthHandlers) AddMethods() { NewHP(h.getResources, "scoped_resources"), NewHP(fetchIdpBasicConfig, "idp", "", "info"), NewHP(fetchIdpSAMLMetadata, "idp", "", "saml-metadata"), + // oidc NewHP(handleOIDCUserInfo, "oidc", "user"), ) h.AddByMethod(POST, FetchAuthToken, diff --git a/pkg/apigateway/handler/middleware.go b/pkg/apigateway/handler/middleware.go index a539ebd177..541c12ffc4 100644 --- a/pkg/apigateway/handler/middleware.go +++ b/pkg/apigateway/handler/middleware.go @@ -121,7 +121,7 @@ func fetchAndSetAuthContext(ctx context.Context, w http.ResponseWriter, r *http. } // no more send auth header, save auth info in cookie // setAuthHeader(w, authHeader) - ctx = context.WithValue(ctx, appctx.AppContextKey(constants.AUTH_TOKEN), token) + ctx = context.WithValue(ctx, appctx.APP_CONTEXT_KEY_AUTH_TOKEN, token) return ctx, nil } diff --git a/pkg/apigateway/handler/proxy.go b/pkg/apigateway/handler/proxy.go index 580d5c2e77..f0e2ed2876 100644 --- a/pkg/apigateway/handler/proxy.go +++ b/pkg/apigateway/handler/proxy.go @@ -19,21 +19,39 @@ import ( "net/http" "net/url" + "yunion.io/x/onecloud/pkg/apis" "yunion.io/x/onecloud/pkg/appsrv" "yunion.io/x/onecloud/pkg/mcclient/auth" "yunion.io/x/onecloud/pkg/proxy" ) type InfluxdbProxyHandler struct { - prefix string + prefix string + serviceName string } func NewInfluxdbProxyHandler(prefix string) *InfluxdbProxyHandler { - return &InfluxdbProxyHandler{prefix: prefix} + return NewProxyHandlerWithService(prefix, apis.SERVICE_TYPE_INFLUXDB) +} + +func NewProxyHandlerWithService(prefix string, serviceName string) *InfluxdbProxyHandler { + return &InfluxdbProxyHandler{ + prefix: prefix, + serviceName: serviceName, + } +} + +func requestManipulator(ctx context.Context, r *http.Request) (*http.Request, error) { + r.Header.Del("Cookie") + token := AppContextToken(ctx) + if token != nil { + r.Header.Set("X-Auth-Token", token.GetTokenString()) + } + return r, nil } func (h *InfluxdbProxyHandler) Bind(app *appsrv.Application) { - app.AddReverseProxyHandler(h.prefix, fetchReverseEndpoint("influxdb")) + app.AddReverseProxyHandler(h.prefix, fetchReverseEndpoint(h.serviceName), requestManipulator) } func getEndpointSchemeHost(endpoint string) (string, error) { @@ -49,7 +67,7 @@ func getEndpointSchemeHost(endpoint string) (string, error) { } func fetchReverseEndpoint(serviceName string) *proxy.SEndpointFactory { - f := func(ctx context.Context, w http.ResponseWriter, r *http.Request) (string, error) { + f := func(ctx context.Context, r *http.Request) (string, error) { endpointType := "internalURL" session := auth.GetAdminSession(ctx, FetchRegion(r), "") ep, err := session.GetServiceURL(serviceName, endpointType) diff --git a/pkg/apis/cloudid/cloudid.go b/pkg/apis/cloudid/cloudid.go index 262b635bbf..3e9cfed33d 100644 --- a/pkg/apis/cloudid/cloudid.go +++ b/pkg/apis/cloudid/cloudid.go @@ -18,5 +18,6 @@ import "yunion.io/x/onecloud/pkg/apis" const ( SERVICE_TYPE = apis.SERVICE_TYPE_CLOUDID + SAML_IDP_PREFIX = "/saml/idp" SERVICE_VERSION = "" ) diff --git a/pkg/apis/cloudid/doc.go b/pkg/apis/cloudid/doc.go index 7d11f48f98..6c334b2c80 100644 --- a/pkg/apis/cloudid/doc.go +++ b/pkg/apis/cloudid/doc.go @@ -1 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + package cloudid // import "yunion.io/x/onecloud/pkg/apis/cloudid" diff --git a/pkg/apis/compute/cloudaccount.go b/pkg/apis/compute/cloudaccount.go index d546386bf0..a68250f470 100644 --- a/pkg/apis/compute/cloudaccount.go +++ b/pkg/apis/compute/cloudaccount.go @@ -399,3 +399,16 @@ type SubscriptonCreateInput struct { type EnrollmentAccountQuery struct { } + +type GetCloudaccountSamlOutput struct { + // cloudaccount SAML ServiceProvider entity ID + EntityId string `json:"entity_id"` + // redirect login URL for this cloudaccount + RedirectLoginUrl string `json:"redirect_login_url"` + // redirect logout URL for this cloudaccount + RedirectLogoutUrl string `json:"redirect_logout_url"` + // metadata URL for this cloudaccount + MetadataUrl string `json:"metadata_url"` + // initial SAML SSO login URL for this cloudaccount + InitLoginUrl string `json:"init_login_url"` +} diff --git a/pkg/apis/const.go b/pkg/apis/const.go index 0de1d188c2..2a9b8da5a6 100644 --- a/pkg/apis/const.go +++ b/pkg/apis/const.go @@ -37,4 +37,5 @@ const ( SERVICE_TYPE_SERVICETREE = "servicetree" SERVICE_TYPE_LOG = "log" SERVICE_TYPE_REGION = "compute" + SERVICE_TYPE_INFLUXDB = "influxdb" ) diff --git a/pkg/appctx/context.go b/pkg/appctx/context.go index 3cfa1e10f9..34b4548a3c 100644 --- a/pkg/appctx/context.go +++ b/pkg/appctx/context.go @@ -41,6 +41,8 @@ const ( APP_CONTEXT_KEY_START_TIME = AppContextKey("starttime") APP_CONTEXT_KEY_HOST_ID = AppContextKey("hostid") + + APP_CONTEXT_KEY_AUTH_TOKEN = AppContextKey("X_AUTH_TOKEN") ) func AppContextServiceName(ctx context.Context) string { diff --git a/pkg/appsrv/appsrv.go b/pkg/appsrv/appsrv.go index cfb77cbea2..91b93e7b83 100644 --- a/pkg/appsrv/appsrv.go +++ b/pkg/appsrv/appsrv.go @@ -143,8 +143,8 @@ func (app *Application) getRoot(method string) *RadixNode { return v } -func (app *Application) AddReverseProxyHandler(prefix string, ef *proxy.SEndpointFactory) { - handler := proxy.NewHTTPReverseProxy(ef).ServeHTTP +func (app *Application) AddReverseProxyHandler(prefix string, ef *proxy.SEndpointFactory, m proxy.RequestManipulator) { + handler := proxy.NewHTTPReverseProxy(ef, m).ServeHTTP for _, method := range []string{"GET", "HEAD", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"} { app.AddHandler(method, prefix, handler) } diff --git a/pkg/appsrv/handlers.go b/pkg/appsrv/handlers.go index 40a5592e0a..a87a0816f9 100644 --- a/pkg/appsrv/handlers.go +++ b/pkg/appsrv/handlers.go @@ -25,6 +25,8 @@ import ( type FilterHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request) +type TMiddleware func(handler FilterHandler) FilterHandler + func VersionHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, version.GetShortString()) } diff --git a/pkg/cloudid/options/doc.go b/pkg/cloudid/options/doc.go index 17ff666c96..66eb2a0f71 100644 --- a/pkg/cloudid/options/doc.go +++ b/pkg/cloudid/options/doc.go @@ -1 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + package options // import "yunion.io/x/onecloud/pkg/cloudid/options" diff --git a/pkg/cloudid/options/options.go b/pkg/cloudid/options/options.go index 780f9dfc1e..581c8169dd 100644 --- a/pkg/cloudid/options/options.go +++ b/pkg/cloudid/options/options.go @@ -25,6 +25,8 @@ type SCloudIdOptions struct { CloudaccountSyncIntervalMinutes int `help:"frequency to sync region cloudaccount task" default:"3"` SystemPoliciesSyncIntervalHours int `help:"frequency to sync region cloudaccount task" default:"24"` CloudIdResourceSyncIntervalHours int `help:"frequency to sync region cloudpolicy task" default:"3"` + + CloudSAMLMetadataPath string `help:"path to store SAML sp metadata file of cloud providers" default:"/opt/yunion/"` } var ( diff --git a/pkg/cloudid/policy/doc.go b/pkg/cloudid/policy/doc.go index 9485bad10d..a0606a61c7 100644 --- a/pkg/cloudid/policy/doc.go +++ b/pkg/cloudid/policy/doc.go @@ -1 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + package policy // import "yunion.io/x/onecloud/pkg/cloudid/policy" diff --git a/pkg/cloudid/saml/doc.go b/pkg/cloudid/saml/doc.go new file mode 100644 index 0000000000..837002ab26 --- /dev/null +++ b/pkg/cloudid/saml/doc.go @@ -0,0 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package saml // import "yunion.io/x/onecloud/pkg/cloudid/saml" diff --git a/pkg/cloudid/saml/init.go b/pkg/cloudid/saml/init.go new file mode 100644 index 0000000000..8e3cd8ddc7 --- /dev/null +++ b/pkg/cloudid/saml/init.go @@ -0,0 +1,82 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package saml + +import ( + "os" + + "yunion.io/x/pkg/errors" + + "yunion.io/x/onecloud/pkg/appsrv" + "yunion.io/x/onecloud/pkg/cloudid/options" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/util/samlutils" + "yunion.io/x/onecloud/pkg/util/samlutils/idp" + "yunion.io/x/onecloud/pkg/util/seclib2" +) + +var ( + saml *samlutils.SSAMLInstance + idpInstance *idp.SSAMLIdpInstance +) + +func initSAMLInstance() error { + certfile := options.Options.SslCertfile + if len(options.Options.SslCaCerts) > 0 { + var err error + certfile, err = seclib2.MergeCaCertFiles(options.Options.SslCaCerts, options.Options.SslCertfile) + if err != nil { + return errors.Wrapf(httperrors.ErrInputParameter, "fail to merge ca+cert content: %s", err) + } + defer os.Remove(certfile) + } + if len(certfile) == 0 { + return errors.Wrap(httperrors.ErrInputParameter, "Missing ssl-certfile") + } + if len(options.Options.SslKeyfile) == 0 { + return errors.Wrap(httperrors.ErrInputParameter, "Missing ssl-keyfile") + } + + var err error + saml, err = samlutils.NewSAMLInstance(options.Options.ApiServer, certfile, options.Options.SslKeyfile) + if err != nil { + return errors.Wrap(err, "samlutils.NewSAMLInstance") + } + + return nil +} + +func SAMLInstance() *samlutils.SSAMLInstance { + if saml.GetEntityId() != options.Options.ApiServer { + saml.SetEntityId(options.Options.ApiServer) + } + return saml +} + +func IsSAMLEnabled() bool { + return saml != nil +} + +func InitSAML(app *appsrv.Application, prefix string) error { + err := initSAMLInstance() + if err != nil { + return errors.Wrap(err, "initSAMLInstance") + } + err = initSAMLIdp(app, prefix) + if err != nil { + return errors.Wrap(err, "initSAMLIdp") + } + return nil +} diff --git a/pkg/cloudid/saml/initidp.go b/pkg/cloudid/saml/initidp.go new file mode 100644 index 0000000000..f226da30ec --- /dev/null +++ b/pkg/cloudid/saml/initidp.go @@ -0,0 +1,113 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package saml + +import ( + "context" + "fmt" + "io/ioutil" + "path" + + "yunion.io/x/log" + "yunion.io/x/pkg/errors" + + "yunion.io/x/onecloud/pkg/appsrv" + "yunion.io/x/onecloud/pkg/cloudid/options" + "yunion.io/x/onecloud/pkg/cloudid/saml/providers" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient/auth" + "yunion.io/x/onecloud/pkg/util/httputils" + "yunion.io/x/onecloud/pkg/util/samlutils" + "yunion.io/x/onecloud/pkg/util/samlutils/idp" +) + +func initSAMLIdp(app *appsrv.Application, prefix string) error { + spFunc := func(ctx context.Context, idpId string, sp *idp.SSAMLServiceProvider) (samlutils.SSAMLSpInitiatedLoginData, error) { + token := auth.FetchUserCredential(ctx, nil) + log.Debugf("Recive SP initiated Login: %s", sp.GetEntityId()) + data := samlutils.SSAMLSpInitiatedLoginData{} + driver := providers.FindDriver(sp.GetEntityId()) + if driver == nil { + return data, errors.Wrapf(httperrors.ErrResourceNotFound, "entityID %s not found", sp.GetEntityId()) + } + data, err := driver.GetSpInitiatedLoginData(idpId, token.GetUserId(), sp) + if err != nil { + return data, errors.Wrap(err, "driver.GetSpInitiatedLoginData") + } + return data, nil + /* + switch sp.GetEntityId() { + case SAML_ENTITY_ID_HUAWEI_CLOUD: // 华为云 SSO + + case SAML_ENTITY_ID_TENCENT_CLOUD: // 腾讯云 role SSO + + } + return data + */ + } + + idpFunc := func(ctx context.Context, sp *idp.SSAMLServiceProvider, idpId string) (samlutils.SSAMLIdpInitiatedLoginData, error) { + token := auth.FetchUserCredential(ctx, nil) + log.Debugf("Recive IDP initiated Login: %s", sp.GetEntityId()) + data := samlutils.SSAMLIdpInitiatedLoginData{} + driver := providers.FindDriver(sp.GetEntityId()) + if driver == nil { + return data, errors.Wrapf(httperrors.ErrResourceNotFound, "entityID %s not found", sp.GetEntityId()) + } + data, err := driver.GetIdpInitiatedLoginData(idpId, token.GetUserId(), sp) + if err != nil { + return data, errors.Wrap(err, "driver.GetIdpInitiatedLoginData") + } + return data, nil + } + + logoutFunc := func(ctx context.Context, idpId string) string { + return fmt.Sprintf(`

成功退出登录,重新登录

`, options.Options.ApiServer) + } + + idpInst := idp.NewIdpInstance(saml, spFunc, idpFunc, logoutFunc) + for entityId, drvFactory := range providers.AllDrivers() { + filePath := path.Join(options.Options.CloudSAMLMetadataPath, drvFactory.GetMetadataFilename()) + metaBytes, err := ioutil.ReadFile(filePath) + if err != nil || len(metaBytes) == 0 { + metaUrl := drvFactory.GetMetadataUrl() + if len(metaUrl) > 0 { + log.Debugf("[%s] metadata file load failed, try download from %s", entityId, metaUrl) + httpcli := httputils.GetDefaultClient() + resp, err := httpcli.Get(metaUrl) + if err != nil { + return errors.Wrapf(err, "http get %s fail", metaUrl) + } + metaBytes, err = ioutil.ReadAll(resp.Body) + if err != nil { + return errors.Wrapf(err, "read body %s fail", metaUrl) + } + } else { + return errors.Wrapf(err, "read file %s fail", filePath) + } + } + err = idpInst.AddSPMetadata(metaBytes) + if err != nil { + return errors.Wrapf(err, "AddSPMetadata %s", metaBytes) + } + } + + idpInst.AddHandlers(app, prefix, auth.Authenticate) + idpInst.SetHtmlTemplate(`

正在跳转到云控制台,请等待。。。

$FORM$`) + + idpInstance = idpInst + + return nil +} diff --git a/pkg/cloudid/saml/load.go b/pkg/cloudid/saml/load.go new file mode 100644 index 0000000000..02e9b5f4ac --- /dev/null +++ b/pkg/cloudid/saml/load.go @@ -0,0 +1,24 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package saml + +import ( + _ "yunion.io/x/onecloud/pkg/cloudid/saml/providers/aliyun" + _ "yunion.io/x/onecloud/pkg/cloudid/saml/providers/aws" + _ "yunion.io/x/onecloud/pkg/cloudid/saml/providers/awscn" + _ "yunion.io/x/onecloud/pkg/cloudid/saml/providers/google" + _ "yunion.io/x/onecloud/pkg/cloudid/saml/providers/huawei" + _ "yunion.io/x/onecloud/pkg/cloudid/saml/providers/qcloud" +) diff --git a/pkg/cloudid/saml/providers/aliyun/doc.go b/pkg/cloudid/saml/providers/aliyun/doc.go new file mode 100644 index 0000000000..5e38945935 --- /dev/null +++ b/pkg/cloudid/saml/providers/aliyun/doc.go @@ -0,0 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package aliyun // import "yunion.io/x/onecloud/pkg/cloudid/saml/providers/aliyun" diff --git a/pkg/cloudid/saml/providers/aliyun/driver.go b/pkg/cloudid/saml/providers/aliyun/driver.go new file mode 100644 index 0000000000..c17cad0020 --- /dev/null +++ b/pkg/cloudid/saml/providers/aliyun/driver.go @@ -0,0 +1,47 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package aliyun + +import ( + "yunion.io/x/pkg/errors" + + "yunion.io/x/onecloud/pkg/util/samlutils" + "yunion.io/x/onecloud/pkg/util/samlutils/idp" +) + +func (d *SAliyunSAMLDriver) GetIdpInitiatedLoginData(cloudAccoutId string, userId string, sp *idp.SSAMLServiceProvider) (samlutils.SSAMLIdpInitiatedLoginData, error) { + // TODO + data := samlutils.SSAMLIdpInitiatedLoginData{} + data.NameId = "ecsossreadonly" + data.NameIdFormat = samlutils.NAME_ID_FORMAT_PERSISTENT + data.AudienceRestriction = sp.GetEntityId() + for k, v := range map[string]string{ + "https://www.aliyun.com/SAML-Role/Attributes/Role": "acs:ram::1123247935774897:role/administrator,acs:ram::1123247935774897:saml-provider/saml.yunion.io", + "https://www.aliyun.com/SAML-Role/Attributes/RoleSessionName": "ecsossreadonly", + "https://www.aliyun.com/SAML-Role/Attributes/SessionDuration": "1800", + } { + data.Attributes = append(data.Attributes, samlutils.SSAMLResponseAttribute{ + Name: k, + Values: []string{v}, + }) + } + data.RelayState = "https://homenew.console.aliyun.com/" + return data, nil +} + +func (d *SAliyunSAMLDriver) GetSpInitiatedLoginData(cloudAccoutId string, userId string, sp *idp.SSAMLServiceProvider) (samlutils.SSAMLSpInitiatedLoginData, error) { + // not supported + return samlutils.SSAMLSpInitiatedLoginData{}, errors.ErrNotSupported +} diff --git a/pkg/cloudid/saml/providers/aliyun/factory.go b/pkg/cloudid/saml/providers/aliyun/factory.go new file mode 100644 index 0000000000..bf5f194459 --- /dev/null +++ b/pkg/cloudid/saml/providers/aliyun/factory.go @@ -0,0 +1,38 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package aliyun + +import ( + "yunion.io/x/onecloud/pkg/cloudid/saml/providers" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SAliyunSAMLDriver struct{} + +func (d *SAliyunSAMLDriver) GetEntityID() string { + return cloudprovider.SAML_ENTITY_ID_ALIYUN_ROLE +} + +func (d *SAliyunSAMLDriver) GetMetadataFilename() string { + return "aliyun_role.xml" +} + +func (d *SAliyunSAMLDriver) GetMetadataUrl() string { + return "https://signin.aliyun.com/saml-role/sp-metadata.xml" +} + +func init() { + providers.Register(&SAliyunSAMLDriver{}) +} diff --git a/pkg/cloudid/saml/providers/aws/doc.go b/pkg/cloudid/saml/providers/aws/doc.go new file mode 100644 index 0000000000..7b67ba4a19 --- /dev/null +++ b/pkg/cloudid/saml/providers/aws/doc.go @@ -0,0 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package aws // import "yunion.io/x/onecloud/pkg/cloudid/saml/providers/aws" diff --git a/pkg/cloudid/saml/providers/aws/driver.go b/pkg/cloudid/saml/providers/aws/driver.go new file mode 100644 index 0000000000..bfae216057 --- /dev/null +++ b/pkg/cloudid/saml/providers/aws/driver.go @@ -0,0 +1,66 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package aws + +import ( + "yunion.io/x/pkg/errors" + + "yunion.io/x/onecloud/pkg/util/samlutils" + "yunion.io/x/onecloud/pkg/util/samlutils/idp" +) + +func (d *SAWSSAMLDriver) GetIdpInitiatedLoginData(cloudAccoutId string, userId string, sp *idp.SSAMLServiceProvider) (samlutils.SSAMLIdpInitiatedLoginData, error) { + // TODO + data := samlutils.SSAMLIdpInitiatedLoginData{} + + data.NameId = "ec2s3readonly" + data.NameIdFormat = samlutils.NAME_ID_FORMAT_PERSISTENT + data.AudienceRestriction = "https://signin.aws.amazon.com/saml" + for _, v := range []struct { + name string + friendlyName string + value string + }{ + { + name: "https://aws.amazon.com/SAML/Attributes/Role", + friendlyName: "RoleEntitlement", + value: "arn:aws:iam::285906155448:role/ec2s3readonly,arn:aws:iam::285906155448:saml-provider/saml.yunion.cn", + }, + { + name: "https://aws.amazon.com/SAML/Attributes/RoleSessionName", + friendlyName: "RoleSessionName", + value: "ec2s3readonly", + }, + { + name: "urn:oid:1.3.6.1.4.1.5923.1.1.1.3", + friendlyName: "eduPersonOrgDN", + value: "ec2s3readonly", + }, + } { + data.Attributes = append(data.Attributes, samlutils.SSAMLResponseAttribute{ + Name: v.name, + FriendlyName: v.friendlyName, + Values: []string{v.value}, + }) + } + data.RelayState = "https://console.aws.amazon.com/" + + return data, nil +} + +func (d *SAWSSAMLDriver) GetSpInitiatedLoginData(cloudAccoutId string, userId string, sp *idp.SSAMLServiceProvider) (samlutils.SSAMLSpInitiatedLoginData, error) { + // not supported + return samlutils.SSAMLSpInitiatedLoginData{}, errors.ErrNotSupported +} diff --git a/pkg/cloudid/saml/providers/aws/factory.go b/pkg/cloudid/saml/providers/aws/factory.go new file mode 100644 index 0000000000..1d1818054c --- /dev/null +++ b/pkg/cloudid/saml/providers/aws/factory.go @@ -0,0 +1,38 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package aws + +import ( + "yunion.io/x/onecloud/pkg/cloudid/saml/providers" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SAWSSAMLDriver struct{} + +func (d *SAWSSAMLDriver) GetEntityID() string { + return cloudprovider.SAML_ENTITY_ID_AWS +} + +func (d *SAWSSAMLDriver) GetMetadataFilename() string { + return "aws.xml" +} + +func (d *SAWSSAMLDriver) GetMetadataUrl() string { + return "https://signin.aws.amazon.com/static/saml-metadata.xml" +} + +func init() { + providers.Register(&SAWSSAMLDriver{}) +} diff --git a/pkg/cloudid/saml/providers/awscn/doc.go b/pkg/cloudid/saml/providers/awscn/doc.go new file mode 100644 index 0000000000..652b0b4631 --- /dev/null +++ b/pkg/cloudid/saml/providers/awscn/doc.go @@ -0,0 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package awscn // import "yunion.io/x/onecloud/pkg/cloudid/saml/providers/awscn" diff --git a/pkg/cloudid/saml/providers/awscn/driver.go b/pkg/cloudid/saml/providers/awscn/driver.go new file mode 100644 index 0000000000..8e3459b677 --- /dev/null +++ b/pkg/cloudid/saml/providers/awscn/driver.go @@ -0,0 +1,66 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package awscn + +import ( + "yunion.io/x/pkg/errors" + + "yunion.io/x/onecloud/pkg/util/samlutils" + "yunion.io/x/onecloud/pkg/util/samlutils/idp" +) + +func (d *SAWSCNSAMLDriver) GetIdpInitiatedLoginData(cloudAccoutId string, userId string, sp *idp.SSAMLServiceProvider) (samlutils.SSAMLIdpInitiatedLoginData, error) { + // TODO + data := samlutils.SSAMLIdpInitiatedLoginData{} + + data.NameId = "ec2s3readonly" + data.NameIdFormat = samlutils.NAME_ID_FORMAT_PERSISTENT + data.AudienceRestriction = "https://signin.amazonaws.cn/saml" + for _, v := range []struct { + name string + friendlyName string + value string + }{ + { + name: "https://aws.amazon.com/SAML/Attributes/Role", + friendlyName: "RoleEntitlement", + value: "arn:aws-cn:iam::248697896586:role/ec2s3readonly,arn:aws-cn:iam::248697896586:saml-provider/saml.yunion.io", + }, + { + name: "https://aws.amazon.com/SAML/Attributes/RoleSessionName", + friendlyName: "RoleSessionName", + value: "ec2s3readonly", + }, + { + name: "urn:oid:1.3.6.1.4.1.5923.1.1.1.3", + friendlyName: "eduPersonOrgDN", + value: "ec2s3readonly", + }, + } { + data.Attributes = append(data.Attributes, samlutils.SSAMLResponseAttribute{ + Name: v.name, + FriendlyName: v.friendlyName, + Values: []string{v.value}, + }) + } + data.RelayState = "https://console.amazonaws.cn/" + + return data, nil +} + +func (d *SAWSCNSAMLDriver) GetSpInitiatedLoginData(cloudAccoutId string, userId string, sp *idp.SSAMLServiceProvider) (samlutils.SSAMLSpInitiatedLoginData, error) { + // not supported + return samlutils.SSAMLSpInitiatedLoginData{}, errors.ErrNotSupported +} diff --git a/pkg/cloudid/saml/providers/awscn/factory.go b/pkg/cloudid/saml/providers/awscn/factory.go new file mode 100644 index 0000000000..473fb57a67 --- /dev/null +++ b/pkg/cloudid/saml/providers/awscn/factory.go @@ -0,0 +1,38 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package awscn + +import ( + "yunion.io/x/onecloud/pkg/cloudid/saml/providers" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SAWSCNSAMLDriver struct{} + +func (d *SAWSCNSAMLDriver) GetEntityID() string { + return cloudprovider.SAML_ENTITY_ID_AWS_CN +} + +func (d *SAWSCNSAMLDriver) GetMetadataFilename() string { + return "aws_cn.xml" +} + +func (d *SAWSCNSAMLDriver) GetMetadataUrl() string { + return "https://signin.amazonaws.cn/static/saml-metadata.xml" +} + +func init() { + providers.Register(&SAWSCNSAMLDriver{}) +} diff --git a/pkg/cloudid/saml/providers/doc.go b/pkg/cloudid/saml/providers/doc.go new file mode 100644 index 0000000000..e1debc3e6b --- /dev/null +++ b/pkg/cloudid/saml/providers/doc.go @@ -0,0 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package providers // import "yunion.io/x/onecloud/pkg/cloudid/saml/providers" diff --git a/pkg/cloudid/saml/providers/google/doc.go b/pkg/cloudid/saml/providers/google/doc.go new file mode 100644 index 0000000000..c50fd369be --- /dev/null +++ b/pkg/cloudid/saml/providers/google/doc.go @@ -0,0 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package google // import "yunion.io/x/onecloud/pkg/cloudid/saml/providers/google" diff --git a/pkg/cloudid/saml/providers/google/driver.go b/pkg/cloudid/saml/providers/google/driver.go new file mode 100644 index 0000000000..5778b5a7a0 --- /dev/null +++ b/pkg/cloudid/saml/providers/google/driver.go @@ -0,0 +1,48 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package google + +import ( + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/util/samlutils" + "yunion.io/x/onecloud/pkg/util/samlutils/idp" +) + +func (d *SGoogleSAMLDriver) GetIdpInitiatedLoginData(cloudAccoutId string, userId string, sp *idp.SSAMLServiceProvider) (samlutils.SSAMLIdpInitiatedLoginData, error) { + // not supported + data := samlutils.SSAMLIdpInitiatedLoginData{} + + return data, httperrors.ErrNotSupported +} + +func (d *SGoogleSAMLDriver) GetSpInitiatedLoginData(cloudAccoutId string, userId string, sp *idp.SSAMLServiceProvider) (samlutils.SSAMLSpInitiatedLoginData, error) { + // TODO + data := samlutils.SSAMLSpInitiatedLoginData{} + + data.NameId = "qiujian@yunion-hk.com" + data.NameIdFormat = samlutils.NAME_ID_FORMAT_EMAIL + data.AudienceRestriction = sp.GetEntityId() + for k, v := range map[string]string{ + "user.email": "qiujian@yunion-hk.com", + } { + data.Attributes = append(data.Attributes, samlutils.SSAMLResponseAttribute{ + Name: k, FriendlyName: k, + NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", + Values: []string{v}, + }) + } + + return data, nil +} diff --git a/pkg/cloudid/saml/providers/google/factory.go b/pkg/cloudid/saml/providers/google/factory.go new file mode 100644 index 0000000000..a058417a4a --- /dev/null +++ b/pkg/cloudid/saml/providers/google/factory.go @@ -0,0 +1,38 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package google + +import ( + "yunion.io/x/onecloud/pkg/cloudid/saml/providers" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SGoogleSAMLDriver struct{} + +func (d *SGoogleSAMLDriver) GetEntityID() string { + return cloudprovider.SAML_ENTITY_ID_GOOGLE +} + +func (d *SGoogleSAMLDriver) GetMetadataFilename() string { + return "gcp.xml" +} + +func (d *SGoogleSAMLDriver) GetMetadataUrl() string { + return "" +} + +func init() { + providers.Register(&SGoogleSAMLDriver{}) +} diff --git a/pkg/cloudid/saml/providers/huawei/doc.go b/pkg/cloudid/saml/providers/huawei/doc.go new file mode 100644 index 0000000000..10153269b5 --- /dev/null +++ b/pkg/cloudid/saml/providers/huawei/doc.go @@ -0,0 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package huawei // import "yunion.io/x/onecloud/pkg/cloudid/saml/providers/huawei" diff --git a/pkg/cloudid/saml/providers/huawei/driver.go b/pkg/cloudid/saml/providers/huawei/driver.go new file mode 100644 index 0000000000..ca773d1bff --- /dev/null +++ b/pkg/cloudid/saml/providers/huawei/driver.go @@ -0,0 +1,49 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package huawei + +import ( + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/util/samlutils" + "yunion.io/x/onecloud/pkg/util/samlutils/idp" +) + +func (d *SHuaweiSAMLDriver) GetIdpInitiatedLoginData(cloudAccoutId string, userId string, sp *idp.SSAMLServiceProvider) (samlutils.SSAMLIdpInitiatedLoginData, error) { + // not supported + data := samlutils.SSAMLIdpInitiatedLoginData{} + + return data, httperrors.ErrNotSupported +} + +func (d *SHuaweiSAMLDriver) GetSpInitiatedLoginData(cloudAccoutId string, userId string, sp *idp.SSAMLServiceProvider) (samlutils.SSAMLSpInitiatedLoginData, error) { + // TODO + data := samlutils.SSAMLSpInitiatedLoginData{} + + data.NameId = "yunionoss" + data.NameIdFormat = samlutils.NAME_ID_FORMAT_TRANSIENT + data.AudienceRestriction = sp.GetEntityId() + for k, v := range map[string]string{ + "User": "ec2admin", + "Group": "ec2admin", + } { + data.Attributes = append(data.Attributes, samlutils.SSAMLResponseAttribute{ + Name: k, FriendlyName: k, + NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", + Values: []string{v}, + }) + } + + return data, nil +} diff --git a/pkg/cloudid/saml/providers/huawei/factory.go b/pkg/cloudid/saml/providers/huawei/factory.go new file mode 100644 index 0000000000..83efab788f --- /dev/null +++ b/pkg/cloudid/saml/providers/huawei/factory.go @@ -0,0 +1,38 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package huawei + +import ( + "yunion.io/x/onecloud/pkg/cloudid/saml/providers" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SHuaweiSAMLDriver struct{} + +func (d *SHuaweiSAMLDriver) GetEntityID() string { + return cloudprovider.SAML_ENTITY_ID_HUAWEI_CLOUD +} + +func (d *SHuaweiSAMLDriver) GetMetadataFilename() string { + return "huawei.xml" +} + +func (d *SHuaweiSAMLDriver) GetMetadataUrl() string { + return "https://auth.huaweicloud.com/authui/saml/metadata.xml" +} + +func init() { + providers.Register(&SHuaweiSAMLDriver{}) +} diff --git a/pkg/cloudid/saml/providers/qcloud/doc.go b/pkg/cloudid/saml/providers/qcloud/doc.go new file mode 100644 index 0000000000..75eceb2ca6 --- /dev/null +++ b/pkg/cloudid/saml/providers/qcloud/doc.go @@ -0,0 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package qcloud // import "yunion.io/x/onecloud/pkg/cloudid/saml/providers/qcloud" diff --git a/pkg/cloudid/saml/providers/qcloud/driver.go b/pkg/cloudid/saml/providers/qcloud/driver.go new file mode 100644 index 0000000000..f6ec7b7d17 --- /dev/null +++ b/pkg/cloudid/saml/providers/qcloud/driver.go @@ -0,0 +1,87 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package qcloud + +import ( + "yunion.io/x/onecloud/pkg/util/samlutils" + "yunion.io/x/onecloud/pkg/util/samlutils/idp" +) + +func (d *SQcloudSAMLDriver) GetIdpInitiatedLoginData(cloudAccoutId string, userId string, sp *idp.SSAMLServiceProvider) (samlutils.SSAMLIdpInitiatedLoginData, error) { + // TODO + data := samlutils.SSAMLIdpInitiatedLoginData{} + + data.NameId = "cvmcosreadonly" + data.NameIdFormat = samlutils.NAME_ID_FORMAT_TRANSIENT + data.AudienceRestriction = "https://cloud.tencent.com" + for _, v := range []struct { + name string + friendlyName string + value string + }{ + { + name: "https://cloud.tencent.com/SAML/Attributes/Role", + friendlyName: "RoleEntitlement", + value: "qcs::cam::uin/100008182714:roleName/cvmcosreadonly,qcs::cam::uin/100008182714:saml-provider/saml.yunion.io", + }, + { + name: "https://cloud.tencent.com/SAML/Attributes/RoleSessionName", + friendlyName: "RoleSessionName", + value: "cvmcosreadonly", + }, + } { + data.Attributes = append(data.Attributes, samlutils.SSAMLResponseAttribute{ + Name: v.name, + FriendlyName: v.friendlyName, + Values: []string{v.value}, + }) + } + data.RelayState = "https://console.cloud.tencent.com/" + + return data, nil +} + +func (d *SQcloudSAMLDriver) GetSpInitiatedLoginData(cloudAccoutId string, userId string, sp *idp.SSAMLServiceProvider) (samlutils.SSAMLSpInitiatedLoginData, error) { + // not supported + data := samlutils.SSAMLSpInitiatedLoginData{} + + data.NameId = "cvmcosreadonly" + data.NameIdFormat = samlutils.NAME_ID_FORMAT_TRANSIENT + data.AudienceRestriction = "https://cloud.tencent.com" + for _, v := range []struct { + name string + friendlyName string + value string + }{ + { + name: "https://cloud.tencent.com/SAML/Attributes/Role", + friendlyName: "RoleEntitlement", + value: "qcs::cam::uin/100008182714:roleName/cvmcosreadonly,qcs::cam::uin/100008182714:saml-provider/saml.yunion.io", + }, + { + name: "https://cloud.tencent.com/SAML/Attributes/RoleSessionName", + friendlyName: "RoleSessionName", + value: "cvmcosreadonly", + }, + } { + data.Attributes = append(data.Attributes, samlutils.SSAMLResponseAttribute{ + Name: v.name, + FriendlyName: v.friendlyName, + Values: []string{v.value}, + }) + } + + return data, nil +} diff --git a/pkg/cloudid/saml/providers/qcloud/factory.go b/pkg/cloudid/saml/providers/qcloud/factory.go new file mode 100644 index 0000000000..a6df41cf0f --- /dev/null +++ b/pkg/cloudid/saml/providers/qcloud/factory.go @@ -0,0 +1,38 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package qcloud + +import ( + "yunion.io/x/onecloud/pkg/cloudid/saml/providers" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SQcloudSAMLDriver struct{} + +func (d *SQcloudSAMLDriver) GetEntityID() string { + return cloudprovider.SAML_ENTITY_ID_QCLOUD +} + +func (d *SQcloudSAMLDriver) GetMetadataFilename() string { + return "qcloud.xml" +} + +func (d *SQcloudSAMLDriver) GetMetadataUrl() string { + return "http://cloud.tencent.com/saml.xml" +} + +func init() { + providers.Register(&SQcloudSAMLDriver{}) +} diff --git a/pkg/cloudid/saml/providers/register.go b/pkg/cloudid/saml/providers/register.go new file mode 100644 index 0000000000..4ecbf79ef4 --- /dev/null +++ b/pkg/cloudid/saml/providers/register.go @@ -0,0 +1,34 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package providers + +var ( + driverTable = make(map[string]ICloudSAMLLoginDriver) +) + +func Register(driver ICloudSAMLLoginDriver) { + driverTable[driver.GetEntityID()] = driver +} + +func FindDriver(entityId string) ICloudSAMLLoginDriver { + if driver, ok := driverTable[entityId]; ok { + return driver + } + return nil +} + +func AllDrivers() map[string]ICloudSAMLLoginDriver { + return driverTable +} diff --git a/pkg/cloudid/saml/providers/types.go b/pkg/cloudid/saml/providers/types.go new file mode 100644 index 0000000000..a7b9b4c29a --- /dev/null +++ b/pkg/cloudid/saml/providers/types.go @@ -0,0 +1,30 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package providers + +import ( + "yunion.io/x/onecloud/pkg/util/samlutils" + "yunion.io/x/onecloud/pkg/util/samlutils/idp" +) + +type ICloudSAMLLoginDriver interface { + GetEntityID() string + + GetMetadataFilename() string + GetMetadataUrl() string + + GetIdpInitiatedLoginData(idpId string, userId string, sp *idp.SSAMLServiceProvider) (samlutils.SSAMLIdpInitiatedLoginData, error) + GetSpInitiatedLoginData(idpId string, userId string, sp *idp.SSAMLServiceProvider) (samlutils.SSAMLSpInitiatedLoginData, error) +} diff --git a/pkg/cloudid/service/doc.go b/pkg/cloudid/service/doc.go index de28f13e34..0f386160de 100644 --- a/pkg/cloudid/service/doc.go +++ b/pkg/cloudid/service/doc.go @@ -1 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + package service // import "yunion.io/x/onecloud/pkg/cloudid/service" diff --git a/pkg/cloudid/service/handlers.go b/pkg/cloudid/service/handlers.go index 0bd8361e6e..fb60fdaf46 100644 --- a/pkg/cloudid/service/handlers.go +++ b/pkg/cloudid/service/handlers.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + package service // Copyright 2019 Yunion diff --git a/pkg/cloudid/service/service.go b/pkg/cloudid/service/service.go index 8dddfe4009..5b3d28ddd0 100644 --- a/pkg/cloudid/service/service.go +++ b/pkg/cloudid/service/service.go @@ -20,6 +20,7 @@ import ( "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/apis/cloudid" "yunion.io/x/onecloud/pkg/cloudcommon" common_app "yunion.io/x/onecloud/pkg/cloudcommon/app" "yunion.io/x/onecloud/pkg/cloudcommon/cronman" @@ -28,6 +29,7 @@ import ( "yunion.io/x/onecloud/pkg/cloudid/models" "yunion.io/x/onecloud/pkg/cloudid/options" _ "yunion.io/x/onecloud/pkg/cloudid/policy" + "yunion.io/x/onecloud/pkg/cloudid/saml" _ "yunion.io/x/onecloud/pkg/cloudid/tasks" _ "yunion.io/x/onecloud/pkg/multicloud/loader" ) @@ -37,7 +39,7 @@ func StartService() { dbOpts := &opts.DBOptions baseOpts := &opts.BaseOptions commonOpts := &opts.CommonOptions - common_options.ParseOptions(opts, os.Args, "cloudid.conf", "cloudid") + common_options.ParseOptions(opts, os.Args, "cloudid.conf", cloudid.SERVICE_TYPE) common_app.InitAuth(commonOpts, func() { log.Infof("Auth complete!!") @@ -49,6 +51,12 @@ func StartService() { db.EnsureAppInitSyncDB(app, dbOpts, models.InitDB) defer cloudcommon.CloseDB() + err := saml.InitSAML(app, cloudid.SAML_IDP_PREFIX) + if err != nil { + log.Errorf("SAML initialization fail %s", err) + return + } + if !opts.IsSlaveNode { cron := cronman.InitCronJobManager(true, options.Options.CronJobWorkerCount) cron.AddJobAtIntervalsWithStartRun("SyncCloudaccounts", time.Duration(opts.CloudaccountSyncIntervalMinutes)*time.Minute, models.CloudaccountManager.SyncCloudaccounts, true) diff --git a/pkg/cloudid/tasks/doc.go b/pkg/cloudid/tasks/doc.go index 715bb89783..5df2a8700d 100644 --- a/pkg/cloudid/tasks/doc.go +++ b/pkg/cloudid/tasks/doc.go @@ -1 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + package tasks // import "yunion.io/x/onecloud/pkg/cloudid/tasks" diff --git a/pkg/cloudprovider/cloudprovider.go b/pkg/cloudprovider/cloudprovider.go index b6e51396fa..4341521ffd 100644 --- a/pkg/cloudprovider/cloudprovider.go +++ b/pkg/cloudprovider/cloudprovider.go @@ -247,6 +247,9 @@ type ICloudProvider interface { GetEnrollmentAccounts() ([]SEnrollmentAccount, error) CreateSubscription(SubscriptionCreateInput) error + + GetSamlEntityId() string + GetSamlSpInitiatedLoginUrl(idpName string) string } func IsSupportProject(prod ICloudProvider) bool { @@ -409,6 +412,14 @@ func (self *SBaseProvider) CreateIProject(name string) (ICloudProject, error) { return nil, ErrNotImplemented } +func (self *SBaseProvider) GetSamlEntityId() string { + return "" +} + +func (self *SBaseProvider) GetSamlSpInitiatedLoginUrl(idpName string) string { + return "" +} + func NewBaseProvider(factory ICloudProviderFactory) SBaseProvider { return SBaseProvider{factory: factory} } diff --git a/pkg/cloudprovider/saml.go b/pkg/cloudprovider/saml.go new file mode 100644 index 0000000000..30012203d7 --- /dev/null +++ b/pkg/cloudprovider/saml.go @@ -0,0 +1,24 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package cloudprovider + +const ( + SAML_ENTITY_ID_ALIYUN_ROLE = "urn:alibaba:cloudcomputing" + SAML_ENTITY_ID_AWS_CN = "urn:amazon:webservices:cn-north-1" + SAML_ENTITY_ID_AWS = "urn:amazon:webservices" + SAML_ENTITY_ID_QCLOUD = "cloud.tencent.com" + SAML_ENTITY_ID_HUAWEI_CLOUD = "https://auth.huaweicloud.com/" + SAML_ENTITY_ID_GOOGLE = "google.com" +) diff --git a/pkg/compute/models/cloudaccount_saml.go b/pkg/compute/models/cloudaccount_saml.go new file mode 100644 index 0000000000..0a623990bd --- /dev/null +++ b/pkg/compute/models/cloudaccount_saml.go @@ -0,0 +1,67 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package models + +import ( + "context" + "fmt" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + "yunion.io/x/onecloud/pkg/apis/cloudid" + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/compute/options" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/httputils" + "yunion.io/x/onecloud/pkg/util/samlutils" +) + +func (account *SCloudaccount) AllowGetDetailsSaml(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return db.IsDomainAllowGetSpec(userCred, account, "saml") +} + +func (account *SCloudaccount) GetDetailsSaml(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (api.GetCloudaccountSamlOutput, error) { + output := api.GetCloudaccountSamlOutput{} + + provider, err := account.GetProvider() + if err != nil { + return output, errors.Wrap(err, "GetProviderFactory") + } + + output.EntityId = provider.GetSamlEntityId() + if len(output.EntityId) == 0 { + return output, errors.Wrap(httperrors.ErrNotSupported, "SAML login not supported") + } + output.RedirectLoginUrl = httputils.JoinPath(options.Options.ApiServer, cloudid.SAML_IDP_PREFIX, "redirect/login", account.Id) + output.RedirectLogoutUrl = httputils.JoinPath(options.Options.ApiServer, cloudid.SAML_IDP_PREFIX, "redirect/logout", account.Id) + output.MetadataUrl = httputils.JoinPath(options.Options.ApiServer, cloudid.SAML_IDP_PREFIX, "metadata", account.Id) + // XXXXX + // TODO, find idpName for this cloudaccount + // XXXXX + idpName := "saml.yunion.io" + output.InitLoginUrl = provider.GetSamlSpInitiatedLoginUrl(idpName) + if len(output.InitLoginUrl) == 0 { + input := samlutils.SIdpInitiatedLoginInput{ + EntityID: output.EntityId, + IdpId: account.Id, + } + output.InitLoginUrl = httputils.JoinPath(options.Options.ApiServer, cloudid.SAML_IDP_PREFIX, fmt.Sprintf("sso?%s", jsonutils.Marshal(input).QueryString())) + } + + return output, nil +} diff --git a/pkg/mcclient/auth/middleware.go b/pkg/mcclient/auth/middleware.go index d22c841dfd..a5d914a362 100644 --- a/pkg/mcclient/auth/middleware.go +++ b/pkg/mcclient/auth/middleware.go @@ -36,7 +36,7 @@ var ( ) const ( - AUTH_TOKEN = appctx.AppContextKey("X_AUTH_TOKEN") + AUTH_TOKEN = appctx.APP_CONTEXT_KEY_AUTH_TOKEN ) type TokenVerifyFunc func(string) (mcclient.TokenCredential, error) @@ -67,7 +67,7 @@ func AuthenticateWithDelayDecision(f appsrv.FilterHandler, delayDecision bool) a } } } - ctx = context.WithValue(ctx, AUTH_TOKEN, token) + ctx = context.WithValue(ctx, appctx.APP_CONTEXT_KEY_AUTH_TOKEN, token) if taskId := r.Header.Get(mcclient.TASK_ID); taskId != "" { ctx = context.WithValue(ctx, appctx.APP_CONTEXT_KEY_TASK_ID, taskId) @@ -81,7 +81,7 @@ func AuthenticateWithDelayDecision(f appsrv.FilterHandler, delayDecision bool) a } func FetchUserCredential(ctx context.Context, filter func(mcclient.TokenCredential) mcclient.TokenCredential) mcclient.TokenCredential { - tokenValue := ctx.Value(AUTH_TOKEN) + tokenValue := ctx.Value(appctx.APP_CONTEXT_KEY_AUTH_TOKEN) if tokenValue != nil { token := tokenValue.(mcclient.TokenCredential) if filter != nil { diff --git a/pkg/monitor/service/dispatcher.go b/pkg/monitor/service/dispatcher.go index c1bda03de5..e2fa37fae2 100644 --- a/pkg/monitor/service/dispatcher.go +++ b/pkg/monitor/service/dispatcher.go @@ -39,7 +39,7 @@ func performHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) _, query, body := fetchEnv(ctx, w, r) appsrv.SendJSON(w, wrap(jsonutils.NewDict(), "subscription")) SubscriptionWorkerManager.Run(func() { - ctx = context.WithValue(context.Background(), auth.AUTH_TOKEN, auth.AdminCredential()) + ctx = context.WithValue(context.Background(), appctx.APP_CONTEXT_KEY_AUTH_TOKEN, auth.AdminCredential()) subscriptionmodel.SubscriptionManager.PerformWrite(ctx, auth.AdminCredential(), query, body) }, nil, nil) diff --git a/pkg/multicloud/aliyun/provider/provider.go b/pkg/multicloud/aliyun/provider/provider.go index eed415378c..866b5f7901 100644 --- a/pkg/multicloud/aliyun/provider/provider.go +++ b/pkg/multicloud/aliyun/provider/provider.go @@ -228,3 +228,11 @@ func (self *SAliyunProvider) GetICustomCloudpolicies() ([]cloudprovider.ICloudpo func (self *SAliyunProvider) CreateICloudpolicy(opts *cloudprovider.SCloudpolicyCreateOptions) (cloudprovider.ICloudpolicy, error) { return self.client.CreateICloudpolicy(opts) } + +func (self *SAliyunProvider) GetSamlEntityId() string { + return cloudprovider.SAML_ENTITY_ID_ALIYUN_ROLE +} + +func (self *SAliyunProvider) GetSamlSpInitiatedLoginUrl(idpName string) string { + return "" +} diff --git a/pkg/multicloud/aws/aws.go b/pkg/multicloud/aws/aws.go index fce1cac503..9929159c36 100644 --- a/pkg/multicloud/aws/aws.go +++ b/pkg/multicloud/aws/aws.go @@ -568,3 +568,12 @@ func (client *SAwsClient) GetObjectCannedAcls() []string { } } } + +func (client *SAwsClient) GetSamlEntityId() string { + switch client.accessUrl { + case AWS_CHINA_CLOUDENV: + return cloudprovider.SAML_ENTITY_ID_AWS_CN + default: + return cloudprovider.SAML_ENTITY_ID_AWS + } +} diff --git a/pkg/multicloud/aws/provider/provider.go b/pkg/multicloud/aws/provider/provider.go index 80eb78f983..ab14e16805 100644 --- a/pkg/multicloud/aws/provider/provider.go +++ b/pkg/multicloud/aws/provider/provider.go @@ -219,3 +219,11 @@ func (self *SAwsProvider) GetIClouduserByName(name string) (cloudprovider.ICloud func (self *SAwsProvider) CreateICloudpolicy(opts *cloudprovider.SCloudpolicyCreateOptions) (cloudprovider.ICloudpolicy, error) { return self.client.CreateICloudpolicy(opts) } + +func (self *SAwsProvider) GetSamlEntityId() string { + return self.client.GetSamlEntityId() +} + +func (self *SAwsProvider) GetSamlSpInitiatedLoginUrl(idpName string) string { + return "" +} diff --git a/pkg/multicloud/google/google.go b/pkg/multicloud/google/google.go index 6fe3352e29..88da05af29 100644 --- a/pkg/multicloud/google/google.go +++ b/pkg/multicloud/google/google.go @@ -911,3 +911,20 @@ func (self *SGoogleClient) GetCapabilities() []string { } return caps } + +func (self *SGoogleClient) GetSamlSpInitiatedLoginUrl(idpName string) string { + // GOOGLE只支持一个IDP, 可以将organization名字存储在idpName里,避免因为权限不足,无法获取organization名称 + if len(idpName) == 0 { + orgs, _ := self.ListOrganizations() + if len(orgs) != 1 { + log.Warningf("Organization count %d != 1, require assign the service account to ONE organization with organization viewer/admin role", len(orgs)) + } else { + idpName = orgs[0].DisplayName + } + } + if len(idpName) == 0 { + log.Errorf("no valid organization name for this GCP account") + return "" + } + return fmt.Sprintf("https://www.google.com/a/%s/ServiceLogin?continue=https://console.cloud.google.com", idpName) +} diff --git a/pkg/multicloud/google/provider/provider.go b/pkg/multicloud/google/provider/provider.go index c44aef5cdd..196690d984 100644 --- a/pkg/multicloud/google/provider/provider.go +++ b/pkg/multicloud/google/provider/provider.go @@ -293,3 +293,11 @@ func (self *SGoogleProvider) GetIClouduserByName(name string) (cloudprovider.ICl func (self *SGoogleProvider) CreateICloudpolicy(opts *cloudprovider.SCloudpolicyCreateOptions) (cloudprovider.ICloudpolicy, error) { return self.client.CreateICloudpolicy(opts) } + +func (self *SGoogleProvider) GetSamlEntityId() string { + return cloudprovider.SAML_ENTITY_ID_GOOGLE +} + +func (self *SGoogleProvider) GetSamlSpInitiatedLoginUrl(idpName string) string { + return self.client.GetSamlSpInitiatedLoginUrl(idpName) +} diff --git a/pkg/multicloud/google/saml.go b/pkg/multicloud/google/saml.go new file mode 100644 index 0000000000..7b311e7f66 --- /dev/null +++ b/pkg/multicloud/google/saml.go @@ -0,0 +1,55 @@ +// Copyright 2019 Yunion +// +// Licensed 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. + +package google + +import ( + "time" + + "yunion.io/x/log" + "yunion.io/x/pkg/errors" +) + +type SOrganizationOwner struct { + DirectoryCustomerId string `json:"directoryCustomerId"` +} + +type SOrganization struct { + OrganizationId string `json:"organizationId"` + DisplayName string `json:"displayName"` + Owner SOrganizationOwner `json:"owner"` + CreationTime time.Time `json:"creationTime"` + LifecycleState string `json:"lifecycleState"` + Name string `json:"name"` +} + +// https://cloud.google.com/resource-manager/reference/rest/v1/organizations/search +// require Organization Viewer privilege +func (self *SGoogleClient) ListOrganizations() ([]SOrganization, error) { + resource := "organizations" + params := map[string]string{ + "pageSize": "1000", + } + resp, err := jsonRequest(self.client, "GET", GOOGLE_MANAGER_DOMAIN, "v1beta1", resource, params, nil, self.debug) + if err != nil { + return nil, errors.Wrap(err, "ListOrganizations") + } + log.Debugf("ListOrganization: %s", resp) + ret := make([]SOrganization, 0) + err = resp.Unmarshal(&ret, "organizations") + if err != nil { + return nil, errors.Wrap(err, "resp.Unmarshal") + } + return ret, nil +} diff --git a/pkg/multicloud/google/shell/resourcepolicy.go b/pkg/multicloud/google/shell/resourcepolicy.go index 39b30714b2..3d6ee48c0c 100644 --- a/pkg/multicloud/google/shell/resourcepolicy.go +++ b/pkg/multicloud/google/shell/resourcepolicy.go @@ -46,4 +46,14 @@ func init() { return nil }) + type ListOrganizationOptions struct{} + shellutils.R(&ListOrganizationOptions{}, "organization-list", "List organizaitons", func(cli *google.SRegion, args *ListOrganizationOptions) error { + orgs, err := cli.GetClient().ListOrganizations() + if err != nil { + return err + } + printList(orgs, 0, 0, 0, nil) + return nil + }) + } diff --git a/pkg/multicloud/huawei/huawei.go b/pkg/multicloud/huawei/huawei.go index 775b5de54b..e39c088c39 100644 --- a/pkg/multicloud/huawei/huawei.go +++ b/pkg/multicloud/huawei/huawei.go @@ -542,3 +542,7 @@ func (self *SHuaweiClient) initOwner() error { self.ownerId = ownerId return nil } + +func (self *SHuaweiClient) GetSamlSpInitiatedLoginUrl(idpName string) string { + return fmt.Sprintf("https://auth.huaweicloud.com/authui/federation/websso?domain_id=%s&idp=%s&protocol=saml", self.ownerId, idpName) +} diff --git a/pkg/multicloud/huawei/provider/provider.go b/pkg/multicloud/huawei/provider/provider.go index 06b372cf7f..50ddca92bb 100644 --- a/pkg/multicloud/huawei/provider/provider.go +++ b/pkg/multicloud/huawei/provider/provider.go @@ -263,3 +263,11 @@ func (self *SHuaweiProvider) GetICustomCloudpolicies() ([]cloudprovider.ICloudpo func (self *SHuaweiProvider) GetIClouduserByName(name string) (cloudprovider.IClouduser, error) { return self.client.GetIClouduserByName(name) } + +func (self *SHuaweiProvider) GetSamlEntityId() string { + return cloudprovider.SAML_ENTITY_ID_HUAWEI_CLOUD +} + +func (self *SHuaweiProvider) GetSamlSpInitiatedLoginUrl(idpName string) string { + return self.client.GetSamlSpInitiatedLoginUrl(idpName) +} diff --git a/pkg/multicloud/qcloud/provider/provider.go b/pkg/multicloud/qcloud/provider/provider.go index 6f459c9d36..587785ee56 100644 --- a/pkg/multicloud/qcloud/provider/provider.go +++ b/pkg/multicloud/qcloud/provider/provider.go @@ -265,3 +265,11 @@ func (self *SQcloudProvider) GetIClouduserByName(name string) (cloudprovider.ICl func (self *SQcloudProvider) CreateICloudpolicy(opts *cloudprovider.SCloudpolicyCreateOptions) (cloudprovider.ICloudpolicy, error) { return self.client.CreateICloudpolicy(opts) } + +func (self *SQcloudProvider) GetSamlEntityId() string { + return cloudprovider.SAML_ENTITY_ID_QCLOUD +} + +func (self *SQcloudProvider) GetSamlSpInitiatedLoginUrl(idpName string) string { + return self.client.GetSamlSpInitiatedLoginUrl(idpName) +} diff --git a/pkg/multicloud/qcloud/qcloud.go b/pkg/multicloud/qcloud/qcloud.go index eb991105cd..aef305b9a6 100644 --- a/pkg/multicloud/qcloud/qcloud.go +++ b/pkg/multicloud/qcloud/qcloud.go @@ -814,3 +814,7 @@ func (self *SQcloudClient) GetCapabilities() []string { } return caps } + +func (self *SQcloudClient) GetSamlSpInitiatedLoginUrl(idpName string) string { + return fmt.Sprintf("https://cloud.tencent.com/login/forwardIdp/%s/%s", self.ownerName, idpName) +} diff --git a/pkg/proxy/http.go b/pkg/proxy/http.go index 41c16d2a03..1c22d4e680 100644 --- a/pkg/proxy/http.go +++ b/pkg/proxy/http.go @@ -26,7 +26,8 @@ import ( "yunion.io/x/onecloud/pkg/httperrors" ) -type EndpointGenerator func(context.Context, http.ResponseWriter, *http.Request) (string, error) +type EndpointGenerator func(context.Context, *http.Request) (string, error) +type RequestManipulator func(ctx context.Context, r *http.Request) (*http.Request, error) type SEndpointFactory struct { generator EndpointGenerator @@ -42,16 +43,18 @@ func NewEndpointFactory(f EndpointGenerator, serviceName string) *SEndpointFacto type SReverseProxy struct { *SEndpointFactory + manipulator RequestManipulator } -func NewHTTPReverseProxy(ef *SEndpointFactory) *SReverseProxy { +func NewHTTPReverseProxy(ef *SEndpointFactory, m RequestManipulator) *SReverseProxy { return &SReverseProxy{ SEndpointFactory: ef, + manipulator: m, } } func (p *SReverseProxy) ServeHTTP(ctx context.Context, w http.ResponseWriter, r *http.Request) { - endpoint, err := p.generator(ctx, w, r) + endpoint, err := p.generator(ctx, r) if err != nil { httperrors.InternalServerError(w, err.Error()) return @@ -68,7 +71,10 @@ func (p *SReverseProxy) ServeHTTP(ctx context.Context, w http.ResponseWriter, r TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, DisableKeepAlives: true, } - r.Header.Del("Cookie") - r.Header.Del("X-Auth-Token") + r, err = p.manipulator(ctx, r) + if err != nil { + httperrors.InternalServerError(w, err.Error()) + return + } proxy.ServeHTTP(w, r) } diff --git a/pkg/s3gateway/handlers/middelware.go b/pkg/s3gateway/handlers/middelware.go index b703eda693..85f2d5a943 100644 --- a/pkg/s3gateway/handlers/middelware.go +++ b/pkg/s3gateway/handlers/middelware.go @@ -45,7 +45,7 @@ func s3authenticate(f appsrv.FilterHandler) appsrv.FilterHandler { SendError(w, Unauthenticated(ctx, err.Error())) return } - ctx = context.WithValue(ctx, auth.AUTH_TOKEN, userCred) + ctx = context.WithValue(ctx, appctx.APP_CONTEXT_KEY_AUTH_TOKEN, userCred) f(ctx, w, r) } diff --git a/pkg/util/httputils/httputils.go b/pkg/util/httputils/httputils.go index 4c027846fa..a78a06d7d0 100644 --- a/pkg/util/httputils/httputils.go +++ b/pkg/util/httputils/httputils.go @@ -786,6 +786,12 @@ func ParseJSONResponse(reqBody string, resp *http.Response, err error, debug boo } } -func JoinPath(ep string, path string) string { - return strings.TrimRight(ep, "/") + "/" + strings.TrimLeft(path, "/") +func JoinPath(ep string, paths ...string) string { + buf := strings.Builder{} + buf.WriteString(strings.TrimRight(ep, "/")) + for _, path := range paths { + buf.WriteByte('/') + buf.WriteString(strings.Trim(path, "/")) + } + return buf.String() } diff --git a/pkg/util/samlutils/demo/service.go b/pkg/util/samlutils/demo/service.go index 823fd480dd..3296aab511 100644 --- a/pkg/util/samlutils/demo/service.go +++ b/pkg/util/samlutils/demo/service.go @@ -22,6 +22,7 @@ import ( "os" "strings" + "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" "yunion.io/x/structarg" @@ -41,6 +42,7 @@ type Options struct { Key string `help:"certificate private key file"` Port int `help:"listening port"` Entity string `help:"SAML entityID"` + IdpId string `help:"IDP ID"` SpMeta []string `help:"ServiceProvider metadata filename"` IdpMeta []string `help:"IdentityProvider metadata filename"` } @@ -105,7 +107,7 @@ func prepareServer() error { return errors.Wrap(err, "NewSAMLInstance") } - spFunc := func(ctx context.Context, sp *idp.SSAMLServiceProvider) samlutils.SSAMLSpInitiatedLoginData { + spFunc := func(ctx context.Context, idpId string, sp *idp.SSAMLServiceProvider) (samlutils.SSAMLSpInitiatedLoginData, error) { log.Debugf("Recive SP initiated Login: %s", sp.GetEntityId()) data := samlutils.SSAMLSpInitiatedLoginData{} switch sp.GetEntityId() { @@ -219,10 +221,10 @@ func prepareServer() error { }) } } - return data + return data, nil } - idpFunc := func(ctx context.Context, sp *idp.SSAMLServiceProvider, state string) samlutils.SSAMLIdpInitiatedLoginData { + idpFunc := func(ctx context.Context, sp *idp.SSAMLServiceProvider, idpId string) (samlutils.SSAMLIdpInitiatedLoginData, error) { log.Debugf("Recive IDP initiated Login: %s", sp.GetEntityId()) data := samlutils.SSAMLIdpInitiatedLoginData{} switch sp.GetEntityId() { @@ -333,10 +335,10 @@ func prepareServer() error { } data.RelayState = "https://console.cloud.tencent.com/" } - return data + return data, nil } - logoutFunc := func(ctx context.Context) string { + logoutFunc := func(ctx context.Context, idpId string) string { return fmt.Sprintf(`

成功退出登录,重新登录

`, httputils.JoinPath(options.Entity, "SAML/idp")) } @@ -347,7 +349,7 @@ func prepareServer() error { return errors.Wrapf(err, "AddSPMetadataFile %s", spMetaFile) } } - idpInst.AddHandlers(app, "SAML/idp") + idpInst.AddHandlers(app, "SAML/idp", nil) idpInst.SetHtmlTemplate(`

正在跳转到云控制台,请等待。。。

$FORM$`) app.AddHandler("GET", "SAML/idp", func(ctx context.Context, w http.ResponseWriter, r *http.Request) { @@ -377,7 +379,11 @@ func prepareServer() error { entityID: "cloud.tencent.com", }, } { - htmlBuf.WriteString(fmt.Sprintf(`
  • %s (IDP-Initiated)
  • `, idpInitUrl, url.QueryEscape(v.entityID), v.name)) + query := samlutils.SIdpInitiatedLoginInput{ + EntityID: v.entityID, + IdpId: options.IdpId, + } + htmlBuf.WriteString(fmt.Sprintf(`
  • %s (IDP-Initiated)
  • `, idpInitUrl, jsonutils.Marshal(query).QueryString(), v.name)) } for _, v := range []struct { diff --git a/pkg/util/samlutils/idp/idp.go b/pkg/util/samlutils/idp/idp.go index e8f0cf8fc1..2133190d16 100644 --- a/pkg/util/samlutils/idp/idp.go +++ b/pkg/util/samlutils/idp/idp.go @@ -25,15 +25,20 @@ import ( "yunion.io/x/log" "yunion.io/x/pkg/errors" + "yunion.io/x/onecloud/pkg/appctx" "yunion.io/x/onecloud/pkg/appsrv" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/util/httputils" "yunion.io/x/onecloud/pkg/util/samlutils" ) -type OnSpInitiatedLogin func(ctx context.Context, sp *SSAMLServiceProvider) samlutils.SSAMLSpInitiatedLoginData -type OnIdpInitiatedLogin func(ctx context.Context, sp *SSAMLServiceProvider, state string) samlutils.SSAMLIdpInitiatedLoginData -type OnLogout func(ctx context.Context) string +const ( + IDP_ID_KEY = "" +) + +type OnSpInitiatedLogin func(ctx context.Context, idpId string, sp *SSAMLServiceProvider) (samlutils.SSAMLSpInitiatedLoginData, error) +type OnIdpInitiatedLogin func(ctx context.Context, sp *SSAMLServiceProvider, IdpId string) (samlutils.SSAMLIdpInitiatedLoginData, error) +type OnLogout func(ctx context.Context, idpId string) string type SSAMLIdpInstance struct { saml *samlutils.SSAMLInstance @@ -61,20 +66,28 @@ func NewIdpInstance(saml *samlutils.SSAMLInstance, spLoginFunc OnSpInitiatedLogi } } -func (idp *SSAMLIdpInstance) AddHandlers(app *appsrv.Application, prefix string) { - idp.metadataPath = httputils.JoinPath(prefix, "metadata") - idp.redirectLoginPath = httputils.JoinPath(prefix, "redirect/login") - idp.redirectLogoutPath = httputils.JoinPath(prefix, "redirect/logout") +func (idp *SSAMLIdpInstance) AddHandlers(app *appsrv.Application, prefix string, middleware appsrv.TMiddleware) { + idp.metadataPath = httputils.JoinPath(prefix, "metadata/"+IDP_ID_KEY) + idp.redirectLoginPath = httputils.JoinPath(prefix, "redirect/login/"+IDP_ID_KEY) + idp.redirectLogoutPath = httputils.JoinPath(prefix, "redirect/logout/"+IDP_ID_KEY) idp.idpInitiatedSSOPath = httputils.JoinPath(prefix, "sso") app.AddHandler("GET", idp.metadataPath, idp.metadataHandler) - app.AddHandler("GET", idp.redirectLoginPath, idp.redirectLoginHandler) - app.AddHandler("GET", idp.redirectLogoutPath, idp.redirectLogoutHandler) + handler := idp.redirectLoginHandler + if middleware != nil { + handler = middleware(handler) + } + app.AddHandler("GET", idp.redirectLoginPath, handler) + handler = idp.redirectLogoutHandler + if middleware != nil { + handler = middleware(handler) + } + app.AddHandler("GET", idp.redirectLogoutPath, handler) app.AddHandler("GET", idp.idpInitiatedSSOPath, idp.idpInitiatedSSOHandler) - log.Infof("IDP metadata: %s", idp.getMetadataUrl()) - log.Infof("IDP redirect login: %s", idp.getRedirectLoginUrl()) - log.Infof("IDP redirect logout: %s", idp.getRedirectLogoutUrl()) + log.Infof("IDP metadata: %s", idp.getMetadataUrl(IDP_ID_KEY)) + log.Infof("IDP redirect login: %s", idp.getRedirectLoginUrl(IDP_ID_KEY)) + log.Infof("IDP redirect logout: %s", idp.getRedirectLogoutUrl(IDP_ID_KEY)) log.Infof("IDP initated SSO: %s", idp.getIdpInitiatedSSOUrl()) } @@ -109,16 +122,16 @@ func (idp *SSAMLIdpInstance) AddSPMetadata(metadata []byte) error { return nil } -func (idp *SSAMLIdpInstance) getMetadataUrl() string { - return httputils.JoinPath(idp.saml.GetEntityId(), idp.metadataPath) +func (idp *SSAMLIdpInstance) getMetadataUrl(idpId string) string { + return strings.Replace(httputils.JoinPath(idp.saml.GetEntityId(), idp.metadataPath), IDP_ID_KEY, idpId, 1) } -func (idp *SSAMLIdpInstance) getRedirectLoginUrl() string { - return httputils.JoinPath(idp.saml.GetEntityId(), idp.redirectLoginPath) +func (idp *SSAMLIdpInstance) getRedirectLoginUrl(idpId string) string { + return strings.Replace(httputils.JoinPath(idp.saml.GetEntityId(), idp.redirectLoginPath), IDP_ID_KEY, idpId, 1) } -func (idp *SSAMLIdpInstance) getRedirectLogoutUrl() string { - return httputils.JoinPath(idp.saml.GetEntityId(), idp.redirectLogoutPath) +func (idp *SSAMLIdpInstance) getRedirectLogoutUrl(idpId string) string { + return strings.Replace(httputils.JoinPath(idp.saml.GetEntityId(), idp.redirectLogoutPath), IDP_ID_KEY, idpId, 1) } func (idp *SSAMLIdpInstance) getIdpInitiatedSSOUrl() string { @@ -126,12 +139,15 @@ func (idp *SSAMLIdpInstance) getIdpInitiatedSSOUrl() string { } func (idp *SSAMLIdpInstance) metadataHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { - desc := idp.getMetadata(ctx) + params := appctx.AppContextParams(ctx) + idpId := params[IDP_ID_KEY] + desc := idp.getMetadata(idpId) appsrv.SendXmlWithIndent(w, nil, desc, true) } func (idp *SSAMLIdpInstance) redirectLoginHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { - _, query, _ := appsrv.FetchEnv(ctx, w, r) + params, query, _ := appsrv.FetchEnv(ctx, w, r) + idpId := params[IDP_ID_KEY] input := samlutils.SIdpRedirectLoginInput{} err := query.Unmarshal(&input) if err != nil { @@ -139,7 +155,7 @@ func (idp *SSAMLIdpInstance) redirectLoginHandler(ctx context.Context, w http.Re return } log.Debugf("recv input %s", input) - respHtml, err := idp.processLoginRequest(ctx, input) + respHtml, err := idp.processLoginRequest(ctx, idpId, input) if err != nil { httperrors.InputParameterError(w, "parse parameter error %s", err) return @@ -148,8 +164,10 @@ func (idp *SSAMLIdpInstance) redirectLoginHandler(ctx context.Context, w http.Re } func (idp *SSAMLIdpInstance) redirectLogoutHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { + params := appctx.AppContextParams(ctx) + idpId := params[IDP_ID_KEY] log.Debugf("logout: %s", r.Header) - html := idp.onLogout(ctx) + html := idp.onLogout(ctx, idpId) appsrv.SendHTML(w, html) } @@ -169,17 +187,17 @@ func (idp *SSAMLIdpInstance) idpInitiatedSSOHandler(ctx context.Context, w http. appsrv.SendHTML(w, respHtml) } -func (idp *SSAMLIdpInstance) getMetadata(ctx context.Context) samlutils.EntityDescriptor { +func (idp *SSAMLIdpInstance) getMetadata(idpId string) samlutils.EntityDescriptor { input := samlutils.SSAMLIdpMetadataInput{ EntityId: idp.saml.GetEntityId(), CertString: idp.saml.GetCertString(), - RedirectLoginUrl: idp.getRedirectLoginUrl(), - RedirectLogoutUrl: idp.getRedirectLogoutUrl(), + RedirectLoginUrl: idp.getRedirectLoginUrl(idpId), + RedirectLogoutUrl: idp.getRedirectLogoutUrl(idpId), } return samlutils.NewIdpMetadata(input) } -func (idp *SSAMLIdpInstance) processLoginRequest(ctx context.Context, input samlutils.SIdpRedirectLoginInput) (string, error) { +func (idp *SSAMLIdpInstance) processLoginRequest(ctx context.Context, idpId string, input samlutils.SIdpRedirectLoginInput) (string, error) { plainText, err := samlutils.SAMLDecode(input.SAMLRequest) if err != nil { return "", errors.Wrap(err, "samlutils.SAMLDecode") @@ -198,15 +216,15 @@ func (idp *SSAMLIdpInstance) processLoginRequest(ctx context.Context, input saml return "", errors.Wrapf(httperrors.ErrResourceNotFound, "issuer %s not found", authReq.Issuer.Issuer) } - if len(authReq.Destination) > 0 && authReq.Destination != idp.getRedirectLoginUrl() { - return "", errors.Wrapf(httperrors.ErrInputParameter, "Destination not match: get %s want %s", authReq.Destination, idp.getRedirectLoginUrl()) + if len(authReq.Destination) > 0 && authReq.Destination != idp.getRedirectLoginUrl(idpId) { + return "", errors.Wrapf(httperrors.ErrInputParameter, "Destination not match: get %s want %s", authReq.Destination, idp.getRedirectLoginUrl(idpId)) } if authReq.AssertionConsumerServiceURL != sp.GetPostAssertionConsumerServiceUrl() { return "", errors.Wrapf(httperrors.ErrInputParameter, "AssertionConsumerServiceURL not match: get %s want %s", authReq.AssertionConsumerServiceURL, sp.GetPostAssertionConsumerServiceUrl()) } - resp, err := idp.getLoginResponse(ctx, authReq, sp) + resp, err := idp.getLoginResponse(ctx, authReq, idpId, sp) if err != nil { return "", errors.Wrap(err, "getLoginResponse") } @@ -255,8 +273,11 @@ func (idp *SSAMLIdpInstance) getServiceProvider(eId string) *SSAMLServiceProvide return nil } -func (idp *SSAMLIdpInstance) getLoginResponse(ctx context.Context, req samlutils.AuthnRequest, sp *SSAMLServiceProvider) (*samlutils.Response, error) { - data := idp.onSpInitiatedLogin(ctx, sp) +func (idp *SSAMLIdpInstance) getLoginResponse(ctx context.Context, req samlutils.AuthnRequest, idpId string, sp *SSAMLServiceProvider) (*samlutils.Response, error) { + data, err := idp.onSpInitiatedLogin(ctx, idpId, sp) + if err != nil { + return nil, errors.Wrap(err, "idp.onSpInitiatedLogin") + } input := samlutils.SSAMLResponseInput{ IssuerCertString: idp.saml.GetCertString(), IssuerEntityId: idp.saml.GetEntityId(), @@ -274,15 +295,10 @@ func (idp *SSAMLIdpInstance) processIdpInitiatedLogin(ctx context.Context, input if sp == nil { return "", errors.Wrapf(httperrors.ErrResourceNotFound, "issuer %s not found", input.EntityID) } - var state []byte - if len(input.State) > 0 { - var err error - state, err = samlutils.SAMLDecode(input.State) - if err != nil { - return "", errors.Wrapf(httperrors.ErrInputParameter, "invalid state %s: %s", input.State, err) - } + data, err := idp.onIdpInitiatedLogin(ctx, sp, input.IdpId) + if err != nil { + return "", errors.Wrap(err, "idp.onIdpInitiatedLogin") } - data := idp.onIdpInitiatedLogin(ctx, sp, string(state)) respInput := samlutils.SSAMLResponseInput{ IssuerCertString: idp.saml.GetCertString(), IssuerEntityId: idp.saml.GetEntityId(), diff --git a/pkg/util/samlutils/types.go b/pkg/util/samlutils/types.go index cd3aff3832..96a45ff338 100644 --- a/pkg/util/samlutils/types.go +++ b/pkg/util/samlutils/types.go @@ -249,7 +249,7 @@ type SIdpRedirectLoginInput struct { type SIdpInitiatedLoginInput struct { EntityID string `json:"EntityID"` - State string `json:"State"` + IdpId string `json:"IdpId"` } type Issuer struct {