yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/hcso/saml_provider.go (about) 1 // Copyright 2019 Yunion 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package hcso 16 17 import ( 18 "fmt" 19 "time" 20 "unicode" 21 22 "yunion.io/x/jsonutils" 23 "yunion.io/x/pkg/errors" 24 "yunion.io/x/pkg/util/stringutils" 25 26 api "yunion.io/x/cloudmux/pkg/apis/cloudid" 27 "yunion.io/x/cloudmux/pkg/cloudprovider" 28 "yunion.io/x/cloudmux/pkg/multicloud" 29 "yunion.io/x/cloudmux/pkg/multicloud/hcso/client/modules" 30 "yunion.io/x/cloudmux/pkg/multicloud/huawei" 31 "yunion.io/x/onecloud/pkg/util/samlutils" 32 ) 33 34 type SAMLProviderLinks struct { 35 Self string 36 Protocols string 37 } 38 39 type SAMLProvider struct { 40 multicloud.SResourceBase 41 huawei.HuaweiTags 42 client *SHuaweiClient 43 44 Id string 45 Links SAMLProviderLinks 46 Description string 47 } 48 49 func (self *SAMLProvider) GetId() string { 50 return self.Id 51 } 52 53 func (self *SAMLProvider) GetGlobalId() string { 54 return self.Id 55 } 56 57 func (self *SAMLProvider) GetName() string { 58 return self.Id 59 } 60 61 func (self *SAMLProvider) GetStatus() string { 62 mapping, _ := self.client.findMapping() 63 if mapping != nil { 64 return api.SAML_PROVIDER_STATUS_AVAILABLE 65 } 66 return api.SAML_PROVIDER_STATUS_UNVALIABLE 67 } 68 69 func (self *SAMLProvider) GetAuthUrl() string { 70 return fmt.Sprintf("https://auth.%s/authui/federation/websso?domain_id=%s&idp=%s&protocol=saml", self.client.endpoints.EndpointDomain, self.client.ownerId, self.Id) 71 } 72 73 func (self *SAMLProvider) Delete() error { 74 return self.client.DeleteSAMLProvider(self.Id) 75 } 76 77 func (self *SAMLProvider) GetMetadataDocument() (*samlutils.EntityDescriptor, error) { 78 info, err := self.client.GetSAMLProviderMetadata(self.Id) 79 if err != nil { 80 return nil, errors.Wrapf(err, "GetSAMLProviderMetadata(%s)", self.Id) 81 } 82 metadata, err := samlutils.ParseMetadata([]byte(info.Data)) 83 if err != nil { 84 return nil, errors.Wrapf(err, "ParseMetadata") 85 } 86 return &metadata, nil 87 } 88 89 func (self *SAMLProvider) UpdateMetadata(metadata samlutils.EntityDescriptor) error { 90 return self.client.UpdateSAMLProviderMetadata(self.Id, metadata.String()) 91 } 92 93 func (self *SHuaweiClient) ListSAMLProviders() ([]SAMLProvider, error) { 94 client, err := self.newGeneralAPIClient() 95 if err != nil { 96 return nil, errors.Wrapf(err, "newGeneralAPIClient") 97 } 98 samls := []SAMLProvider{} 99 err = doListAllWithNextLink(client.SAMLProviders.List, nil, &samls) 100 if err != nil { 101 return nil, errors.Wrapf(err, "doListAll") 102 } 103 return samls, nil 104 } 105 106 type SAMLProviderProtocol struct { 107 MappingId string 108 Id string 109 } 110 111 func (self *SHuaweiClient) GetSAMLProviderProtocols(id string) ([]SAMLProviderProtocol, error) { 112 client, err := self.newGeneralAPIClient() 113 if err != nil { 114 return nil, errors.Wrap(err, "newGeneralAPIClient") 115 } 116 resp, err := client.SAMLProviders.ListInContextWithSpec(nil, fmt.Sprintf("%s/protocols", id), nil, "protocols") 117 if err != nil { 118 return nil, errors.Wrapf(err, "ListInContextWithSpec") 119 } 120 protocols := []SAMLProviderProtocol{} 121 return protocols, jsonutils.Update(&protocols, resp.Data) 122 } 123 124 func (self *SHuaweiClient) DeleteSAMLProviderProtocol(spId, id string) error { 125 client, err := self.newGeneralAPIClient() 126 if err != nil { 127 return errors.Wrap(err, "newGeneralAPIClient") 128 } 129 _, err = client.SAMLProviders.DeleteInContextWithSpec(nil, spId, fmt.Sprintf("protocols/%s", id), nil, nil, "") 130 return err 131 } 132 133 type SAMLProviderMetadata struct { 134 DomainId string 135 UpdateTime time.Time 136 Data string 137 IdpId string 138 ProtocolId string 139 Id string 140 EntityId string 141 XaccountType string 142 } 143 144 func (self *SHuaweiClient) GetSAMLProviderMetadata(id string) (*SAMLProviderMetadata, error) { 145 client, err := self.newGeneralAPIClient() 146 if err != nil { 147 return nil, errors.Wrap(err, "newGeneralAPIClient") 148 } 149 client.SAMLProviders.SetVersion("v3-ext/OS-FEDERATION") 150 resp, err := client.SAMLProviders.GetInContextWithSpec(nil, id, fmt.Sprintf("protocols/saml/metadata"), nil, "") 151 if err != nil { 152 return nil, err 153 } 154 155 metadata := &SAMLProviderMetadata{} 156 err = resp.Unmarshal(metadata) 157 if err != nil { 158 return nil, errors.Wrap(err, "resp.Unmarshal") 159 } 160 return metadata, nil 161 } 162 163 func (self *SHuaweiClient) UpdateSAMLProviderMetadata(id, metadata string) error { 164 params := map[string]string{ 165 "domain_id": self.ownerId, 166 "xaccount_type": "", 167 "metadata": metadata, 168 } 169 client, err := self.newGeneralAPIClient() 170 if err != nil { 171 return errors.Wrap(err, "newGeneralAPIClient") 172 } 173 client.SAMLProviders.SetVersion("v3-ext/OS-FEDERATION") 174 _, err = client.SAMLProviders.PerformAction2("protocols/saml/metadata", id, jsonutils.Marshal(params), "") 175 if err != nil { 176 return errors.Wrapf(err, "SAMLProvider.PerformAction") 177 } 178 return nil 179 } 180 181 func (self *SHuaweiClient) GetICloudSAMLProviders() ([]cloudprovider.ICloudSAMLProvider, error) { 182 samls, err := self.ListSAMLProviders() 183 if err != nil { 184 return nil, errors.Wrapf(err, "ListSAMLProviders") 185 } 186 ret := []cloudprovider.ICloudSAMLProvider{} 187 for i := range samls { 188 samls[i].client = self 189 ret = append(ret, &samls[i]) 190 } 191 return ret, nil 192 } 193 194 func (self *SHuaweiClient) DeleteSAMLProvider(id string) error { 195 client, err := self.newGeneralAPIClient() 196 if err != nil { 197 return errors.Wrap(err, "newGeneralAPIClient") 198 } 199 _, err = client.SAMLProviders.Delete(id, nil) 200 return err 201 } 202 203 func (self *SHuaweiClient) CreateSAMLProvider(opts *cloudprovider.SAMLProviderCreateOptions) (*SAMLProvider, error) { 204 client, err := self.newGeneralAPIClient() 205 if err != nil { 206 return nil, errors.Wrap(err, "newGeneralAPIClient") 207 } 208 params := jsonutils.Marshal(map[string]interface{}{ 209 "identity_provider": map[string]interface{}{ 210 "description": opts.Name, 211 "enabled": true, 212 }, 213 }) 214 opts.Name = fmt.Sprintf("%s-%s", self.ownerName, opts.Name) 215 name := []byte{} 216 for _, c := range opts.Name { 217 if unicode.IsLetter(c) || unicode.IsNumber(c) || c == '-' || c == '_' { 218 name = append(name, byte(c)) 219 } else { 220 name = append(name, '-') 221 } 222 } 223 opts.Name = string(name) 224 _, err = client.SAMLProviders.Update(opts.Name, params) 225 if err != nil { 226 if he, ok := err.(*modules.HuaweiClientError); ok && he.Code != 409 { 227 return nil, errors.Wrapf(err, "SAMLProviders.Update") 228 } 229 } 230 ret := SAMLProvider{client: self, Id: opts.Name} 231 err = self.UpdateSAMLProviderMetadata(opts.Name, opts.Metadata.String()) 232 if err != nil { 233 return nil, errors.Wrapf(err, "resp.Unmarshal") 234 } 235 err = self.InitSAMLProviderMapping(opts.Name) 236 if err != nil { 237 return nil, errors.Wrapf(err, "InitSAMLProviderMapping") 238 } 239 return &ret, nil 240 } 241 242 type SAMLProviderMapping struct { 243 Id string 244 Rules jsonutils.JSONObject 245 } 246 247 var ( 248 onecloudMappingRules = jsonutils.Marshal(map[string]interface{}{ 249 "rules": []map[string]interface{}{ 250 { 251 "remote": []map[string]interface{}{ 252 { 253 "type": "User", 254 }, 255 { 256 "type": "Groups", 257 }, 258 }, 259 "local": []map[string]interface{}{ 260 { 261 "groups": "{1}", 262 "user": map[string]string{"name": "{0}"}, 263 }, 264 }, 265 }, 266 }, 267 }) 268 ) 269 270 func (self *SHuaweiClient) ListSAMLProviderMappings() ([]SAMLProviderMapping, error) { 271 client, err := self.newGeneralAPIClient() 272 if err != nil { 273 return nil, errors.Wrap(err, "newGeneralAPIClient") 274 } 275 mappings := []SAMLProviderMapping{} 276 err = doListAllWithNextLink(client.SAMLProviderMappings.List, nil, &mappings) 277 if err != nil { 278 return nil, err 279 } 280 return mappings, nil 281 } 282 283 func (self *SHuaweiClient) findMapping() (*SAMLProviderMapping, error) { 284 mappings, err := self.ListSAMLProviderMappings() 285 if err != nil { 286 return nil, errors.Wrapf(err, "ListSAMLProviderMappings") 287 } 288 for i := range mappings { 289 if jsonutils.Marshal(map[string]interface{}{"rules": mappings[i].Rules}).Equals(jsonutils.Marshal(onecloudMappingRules)) { 290 return &mappings[i], nil 291 } 292 } 293 return nil, cloudprovider.ErrNotFound 294 } 295 296 func (self *SHuaweiClient) InitSAMLProviderMapping(spId string) error { 297 client, err := self.newGeneralAPIClient() 298 if err != nil { 299 return errors.Wrap(err, "newGeneralAPIClient") 300 } 301 302 mapping, err := self.findMapping() 303 if err != nil { 304 if errors.Cause(err) != cloudprovider.ErrNotFound { 305 return errors.Wrapf(err, "findMapping") 306 } 307 mappingId := stringutils.UUID4() 308 params := map[string]interface{}{ 309 "mapping": onecloudMappingRules, 310 } 311 _, err = client.SAMLProviderMappings.Update(mappingId, jsonutils.Marshal(params)) 312 if err != nil { 313 return errors.Wrapf(err, "create mapping") 314 } 315 mapping = &SAMLProviderMapping{ 316 Id: mappingId, 317 Rules: onecloudMappingRules, 318 } 319 } 320 protocols, err := self.GetSAMLProviderProtocols(spId) 321 if err != nil { 322 return errors.Wrapf(err, "GetSAMLProviderProtocols") 323 } 324 params := map[string]interface{}{ 325 "protocol": map[string]string{ 326 "mapping_id": mapping.Id, 327 }, 328 } 329 for i := range protocols { 330 if protocols[i].Id == "saml" { 331 if protocols[i].MappingId == mapping.Id { 332 return nil 333 } 334 _, err = client.SAMLProviders.PatchInContextWithSpec(nil, spId, "protocols/saml", jsonutils.Marshal(params), "") 335 return err 336 } 337 } 338 _, err = client.SAMLProviders.UpdateInContextWithSpec(nil, spId, "protocols/saml", jsonutils.Marshal(params), "") 339 return err 340 } 341 342 func (self *SHuaweiClient) DeleteSAMLProviderMapping(id string) error { 343 client, err := self.newGeneralAPIClient() 344 if err != nil { 345 return errors.Wrap(err, "newGeneralAPIClient") 346 } 347 348 _, err = client.SAMLProviderMappings.Delete(id, nil) 349 return err 350 }