yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/huawei/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 huawei 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/huawei/client/modules" 30 "yunion.io/x/onecloud/pkg/util/samlutils" 31 ) 32 33 type SAMLProviderLinks struct { 34 Self string 35 Protocols string 36 } 37 38 type SAMLProvider struct { 39 multicloud.SResourceBase 40 HuaweiTags 41 client *SHuaweiClient 42 43 Id string 44 Links SAMLProviderLinks 45 Description string 46 } 47 48 func (self *SAMLProvider) GetId() string { 49 return self.Id 50 } 51 52 func (self *SAMLProvider) GetGlobalId() string { 53 return self.Id 54 } 55 56 func (self *SAMLProvider) GetName() string { 57 return self.Id 58 } 59 60 func (self *SAMLProvider) GetStatus() string { 61 mapping, _ := self.client.findMapping() 62 if mapping != nil { 63 return api.SAML_PROVIDER_STATUS_AVAILABLE 64 } 65 return api.SAML_PROVIDER_STATUS_UNVALIABLE 66 } 67 68 func (self *SAMLProvider) GetAuthUrl() string { 69 return fmt.Sprintf("https://auth.huaweicloud.com/authui/federation/websso?domain_id=%s&idp=%s&protocol=saml", self.client.ownerId, self.Id) 70 } 71 72 func (self *SAMLProvider) Delete() error { 73 return self.client.DeleteSAMLProvider(self.Id) 74 } 75 76 func (self *SAMLProvider) GetMetadataDocument() (*samlutils.EntityDescriptor, error) { 77 info, err := self.client.GetSAMLProviderMetadata(self.Id) 78 if err != nil { 79 return nil, errors.Wrapf(err, "GetSAMLProviderMetadata(%s)", self.Id) 80 } 81 metadata, err := samlutils.ParseMetadata([]byte(info.Data)) 82 if err != nil { 83 return nil, errors.Wrapf(err, "ParseMetadata") 84 } 85 return &metadata, nil 86 } 87 88 func (self *SAMLProvider) UpdateMetadata(metadata samlutils.EntityDescriptor) error { 89 return self.client.UpdateSAMLProviderMetadata(self.Id, metadata.String()) 90 } 91 92 func (self *SHuaweiClient) ListSAMLProviders() ([]SAMLProvider, error) { 93 client, err := self.newGeneralAPIClient() 94 if err != nil { 95 return nil, errors.Wrapf(err, "newGeneralAPIClient") 96 } 97 samls := []SAMLProvider{} 98 err = doListAllWithNextLink(client.SAMLProviders.List, nil, &samls) 99 if err != nil { 100 return nil, errors.Wrapf(err, "doListAll") 101 } 102 return samls, nil 103 } 104 105 type SAMLProviderProtocol struct { 106 MappingId string 107 Id string 108 } 109 110 func (self *SHuaweiClient) GetSAMLProviderProtocols(id string) ([]SAMLProviderProtocol, error) { 111 client, err := self.newGeneralAPIClient() 112 if err != nil { 113 return nil, errors.Wrap(err, "newGeneralAPIClient") 114 } 115 resp, err := client.SAMLProviders.ListInContextWithSpec(nil, fmt.Sprintf("%s/protocols", id), nil, "protocols") 116 if err != nil { 117 return nil, errors.Wrapf(err, "ListInContextWithSpec") 118 } 119 protocols := []SAMLProviderProtocol{} 120 return protocols, jsonutils.Update(&protocols, resp.Data) 121 } 122 123 func (self *SHuaweiClient) DeleteSAMLProviderProtocol(spId, id string) error { 124 client, err := self.newGeneralAPIClient() 125 if err != nil { 126 return errors.Wrap(err, "newGeneralAPIClient") 127 } 128 _, err = client.SAMLProviders.DeleteInContextWithSpec(nil, spId, fmt.Sprintf("protocols/%s", id), nil, nil, "") 129 return err 130 } 131 132 type SAMLProviderMetadata struct { 133 DomainId string 134 UpdateTime time.Time 135 Data string 136 IdpId string 137 ProtocolId string 138 Id string 139 EntityId string 140 XaccountType string 141 } 142 143 func (self *SHuaweiClient) GetSAMLProviderMetadata(id string) (*SAMLProviderMetadata, error) { 144 client, err := self.newGeneralAPIClient() 145 if err != nil { 146 return nil, errors.Wrap(err, "newGeneralAPIClient") 147 } 148 client.SAMLProviders.SetVersion("v3-ext/OS-FEDERATION") 149 resp, err := client.SAMLProviders.GetInContextWithSpec(nil, id, fmt.Sprintf("protocols/saml/metadata"), nil, "") 150 if err != nil { 151 return nil, err 152 } 153 154 metadata := &SAMLProviderMetadata{} 155 err = resp.Unmarshal(metadata) 156 if err != nil { 157 return nil, errors.Wrap(err, "resp.Unmarshal") 158 } 159 return metadata, nil 160 } 161 162 func (self *SHuaweiClient) UpdateSAMLProviderMetadata(id, metadata string) error { 163 params := map[string]string{ 164 "domain_id": self.ownerId, 165 "xaccount_type": "", 166 "metadata": metadata, 167 } 168 client, err := self.newGeneralAPIClient() 169 if err != nil { 170 return errors.Wrap(err, "newGeneralAPIClient") 171 } 172 client.SAMLProviders.SetVersion("v3-ext/OS-FEDERATION") 173 _, err = client.SAMLProviders.PerformAction2("protocols/saml/metadata", id, jsonutils.Marshal(params), "") 174 if err != nil { 175 return errors.Wrapf(err, "SAMLProvider.PerformAction") 176 } 177 return nil 178 } 179 180 func (self *SHuaweiClient) GetICloudSAMLProviders() ([]cloudprovider.ICloudSAMLProvider, error) { 181 samls, err := self.ListSAMLProviders() 182 if err != nil { 183 return nil, errors.Wrapf(err, "ListSAMLProviders") 184 } 185 ret := []cloudprovider.ICloudSAMLProvider{} 186 for i := range samls { 187 samls[i].client = self 188 ret = append(ret, &samls[i]) 189 } 190 return ret, nil 191 } 192 193 func (self *SHuaweiClient) DeleteSAMLProvider(id string) error { 194 client, err := self.newGeneralAPIClient() 195 if err != nil { 196 return errors.Wrap(err, "newGeneralAPIClient") 197 } 198 _, err = client.SAMLProviders.Delete(id, nil) 199 return err 200 } 201 202 func (self *SHuaweiClient) CreateSAMLProvider(opts *cloudprovider.SAMLProviderCreateOptions) (*SAMLProvider, error) { 203 client, err := self.newGeneralAPIClient() 204 if err != nil { 205 return nil, errors.Wrap(err, "newGeneralAPIClient") 206 } 207 params := jsonutils.Marshal(map[string]interface{}{ 208 "identity_provider": map[string]interface{}{ 209 "description": opts.Name, 210 "enabled": true, 211 }, 212 }) 213 name := []byte{} 214 for _, c := range opts.Name { 215 if unicode.IsLetter(c) || unicode.IsNumber(c) || c == '-' || c == '_' { 216 name = append(name, byte(c)) 217 } else { 218 name = append(name, '-') 219 } 220 } 221 samlName := string(name) 222 err = func() error { 223 idx := 1 224 for { 225 _, err = client.SAMLProviders.Update(samlName, params) 226 if err == nil { 227 return nil 228 } 229 he, ok := err.(*modules.HuaweiClientError) 230 if !ok { 231 return errors.Wrapf(err, "SAMLProviders.Update") 232 } 233 if he.Code != 409 { 234 return errors.Wrapf(err, "SAMLProviders.Update") 235 } 236 samlName = fmt.Sprintf("%s-%d", string(name), idx) 237 idx++ 238 if idx >= 40 { 239 break 240 } 241 } 242 return err 243 }() 244 if err != nil { 245 return nil, errors.Wrapf(err, "saml provider create") 246 } 247 ret := SAMLProvider{client: self, Id: samlName} 248 err = self.UpdateSAMLProviderMetadata(samlName, opts.Metadata.String()) 249 if err != nil { 250 return nil, errors.Wrapf(err, "resp.Unmarshal") 251 } 252 err = self.InitSAMLProviderMapping(samlName) 253 if err != nil { 254 return nil, errors.Wrapf(err, "InitSAMLProviderMapping") 255 } 256 return &ret, nil 257 } 258 259 type SAMLProviderMapping struct { 260 Id string 261 Rules jsonutils.JSONObject 262 } 263 264 var ( 265 onecloudMappingRules = jsonutils.Marshal(map[string]interface{}{ 266 "rules": []map[string]interface{}{ 267 { 268 "remote": []map[string]interface{}{ 269 { 270 "type": "User", 271 }, 272 { 273 "type": "Groups", 274 }, 275 }, 276 "local": []map[string]interface{}{ 277 { 278 "groups": "{1}", 279 "user": map[string]string{"name": "{0}"}, 280 }, 281 }, 282 }, 283 }, 284 }) 285 ) 286 287 func (self *SHuaweiClient) ListSAMLProviderMappings() ([]SAMLProviderMapping, error) { 288 client, err := self.newGeneralAPIClient() 289 if err != nil { 290 return nil, errors.Wrap(err, "newGeneralAPIClient") 291 } 292 mappings := []SAMLProviderMapping{} 293 err = doListAllWithNextLink(client.SAMLProviderMappings.List, nil, &mappings) 294 if err != nil { 295 return nil, err 296 } 297 return mappings, nil 298 } 299 300 func (self *SHuaweiClient) findMapping() (*SAMLProviderMapping, error) { 301 mappings, err := self.ListSAMLProviderMappings() 302 if err != nil { 303 return nil, errors.Wrapf(err, "ListSAMLProviderMappings") 304 } 305 for i := range mappings { 306 if jsonutils.Marshal(map[string]interface{}{"rules": mappings[i].Rules}).Equals(jsonutils.Marshal(onecloudMappingRules)) { 307 return &mappings[i], nil 308 } 309 } 310 return nil, cloudprovider.ErrNotFound 311 } 312 313 func (self *SHuaweiClient) InitSAMLProviderMapping(spId string) error { 314 client, err := self.newGeneralAPIClient() 315 if err != nil { 316 return errors.Wrap(err, "newGeneralAPIClient") 317 } 318 319 mapping, err := self.findMapping() 320 if err != nil { 321 if errors.Cause(err) != cloudprovider.ErrNotFound { 322 return errors.Wrapf(err, "findMapping") 323 } 324 mappingId := stringutils.UUID4() 325 params := map[string]interface{}{ 326 "mapping": onecloudMappingRules, 327 } 328 _, err = client.SAMLProviderMappings.Update(mappingId, jsonutils.Marshal(params)) 329 if err != nil { 330 return errors.Wrapf(err, "create mapping") 331 } 332 mapping = &SAMLProviderMapping{ 333 Id: mappingId, 334 Rules: onecloudMappingRules, 335 } 336 } 337 protocols, err := self.GetSAMLProviderProtocols(spId) 338 if err != nil { 339 return errors.Wrapf(err, "GetSAMLProviderProtocols") 340 } 341 params := map[string]interface{}{ 342 "protocol": map[string]string{ 343 "mapping_id": mapping.Id, 344 }, 345 } 346 for i := range protocols { 347 if protocols[i].Id == "saml" { 348 if protocols[i].MappingId == mapping.Id { 349 return nil 350 } 351 _, err = client.SAMLProviders.PatchInContextWithSpec(nil, spId, "protocols/saml", jsonutils.Marshal(params), "") 352 return err 353 } 354 } 355 _, err = client.SAMLProviders.UpdateInContextWithSpec(nil, spId, "protocols/saml", jsonutils.Marshal(params), "") 356 return err 357 } 358 359 func (self *SHuaweiClient) DeleteSAMLProviderMapping(id string) error { 360 client, err := self.newGeneralAPIClient() 361 if err != nil { 362 return errors.Wrap(err, "newGeneralAPIClient") 363 } 364 365 _, err = client.SAMLProviderMappings.Delete(id, nil) 366 return err 367 }