github.com/Venafi/vcert/v5@v5.10.2/pkg/venafi/cloud/connectorPolicy.go (about) 1 package cloud 2 3 import ( 4 "fmt" 5 "log" 6 "net/http" 7 8 "github.com/Venafi/vcert/v5/pkg/policy" 9 ) 10 11 func (c *Connector) GetPolicy(name string) (*policy.PolicySpecification, error) { 12 if !c.isAuthenticated() { 13 return nil, fmt.Errorf("must be autheticated to request a certificate") 14 } 15 16 cit, err := retrievePolicySpecification(c, name) 17 if err != nil { 18 return nil, err 19 } 20 21 info, err := getCertificateAuthorityInfoFromCloud(cit.CertificateAuthority, cit.CertificateAuthorityAccountId, cit.CertificateAuthorityProductOptionId, c) 22 23 if err != nil { 24 return nil, err 25 } 26 27 log.Println("Building policy") 28 ps := buildPolicySpecification(cit, info, true) 29 30 // getting the users to set to the PolicySpecification 31 policyUsers, err := c.getUsers() 32 if err != nil { 33 return nil, err 34 } 35 ps.Users = policyUsers 36 37 return ps, nil 38 } 39 40 func (c *Connector) SetPolicy(name string, ps *policy.PolicySpecification) (string, error) { 41 if !c.isAuthenticated() { 42 return "", fmt.Errorf("must be autheticated to request a certificate") 43 } 44 45 err := policy.ValidateCloudPolicySpecification(ps) 46 if err != nil { 47 return "", err 48 } 49 50 log.Printf("policy specification is valid") 51 52 var status string 53 54 //validate if zone name is set and if zone already exist on Venafi cloud if not create it. 55 citName := policy.GetCitName(name) 56 57 if citName == "" { 58 return "", fmt.Errorf("cit name is empty, please provide zone in the format: app_name\\cit_name") 59 } 60 61 //get certificate authority product option io 62 var caDetails *policy.CADetails 63 64 if ps.Policy != nil && ps.Policy.CertificateAuthority != nil && *(ps.Policy.CertificateAuthority) != "" { 65 caDetails, err = getCertificateAuthorityDetails(*(ps.Policy.CertificateAuthority), c) 66 67 if err != nil { 68 return "", err 69 } 70 71 } else { 72 if ps.Policy != nil { 73 74 defaultCA := policy.DefaultCA 75 ps.Policy.CertificateAuthority = &defaultCA 76 77 caDetails, err = getCertificateAuthorityDetails(*(ps.Policy.CertificateAuthority), c) 78 if err != nil { 79 return "", err 80 } 81 82 } else { 83 //policy is not specified so we get the default CA 84 caDetails, err = getCertificateAuthorityDetails(policy.DefaultCA, c) 85 if err != nil { 86 return "", err 87 } 88 } 89 } 90 91 //at this moment we know that ps.Policy.CertificateAuthority is valid. 92 93 req, err := policy.BuildCloudCitRequest(ps, caDetails) 94 if err != nil { 95 return "", err 96 } 97 req.Name = citName 98 99 url := c.getURL(urlIssuingTemplate) 100 101 cit, err := getCit(c, citName) 102 103 if err != nil { 104 return "", err 105 } 106 107 if cit != nil { 108 log.Printf("updating issuing template: %s", citName) 109 //update cit using the new values 110 url = fmt.Sprint(url, "/", cit.ID) 111 statusCode, status, body, err := c.request("PUT", url, req) 112 113 if err != nil { 114 return "", err 115 } 116 117 cit, err = parseCitResult(http.StatusOK, statusCode, status, body) 118 119 if err != nil { 120 return status, err 121 } 122 123 } else { 124 log.Printf("creating issuing template: %s", citName) 125 //var body []byte 126 statusCode, status, body, err := c.request("POST", url, req) 127 128 if err != nil { 129 return "", err 130 } 131 132 cit, err = parseCitResult(http.StatusCreated, statusCode, status, body) 133 134 if err != nil { 135 return status, err 136 } 137 138 } 139 140 //validate if appName is set and if app already exist on Venafi cloud if not create it 141 //and as final steps link the app with the cit. 142 appName := policy.GetApplicationName(name) 143 144 if appName == "" { 145 return "", fmt.Errorf("application name is empty, please provide zone in the format: app_name\\cit_name") 146 } 147 148 appDetails, statusCode, err := c.getAppDetailsByName(appName) 149 150 if err != nil && statusCode == 404 { //means application was not found. 151 log.Printf("creating application: %s", appName) 152 153 _, err = c.createApplication(appName, ps, cit) 154 if err != nil { 155 return "", err 156 } 157 158 } else { //determine if the application needs to be updated 159 log.Printf("updating application: %s", appName) 160 err = c.updateApplication(name, ps, cit, appDetails) 161 if err != nil { 162 return "", err 163 } 164 } 165 166 log.Printf("policy successfully applied to %s", name) 167 168 return status, nil 169 } 170 171 func (c *Connector) GetPolicyWithRegex(name string) (*policy.PolicySpecification, error) { 172 173 cit, err := retrievePolicySpecification(c, name) 174 175 if err != nil { 176 return nil, err 177 } 178 179 info, err := getCertificateAuthorityInfoFromCloud(cit.CertificateAuthority, cit.CertificateAuthorityAccountId, cit.CertificateAuthorityProductOptionId, c) 180 181 if err != nil { 182 return nil, err 183 } 184 185 log.Println("Building policy") 186 ps := buildPolicySpecification(cit, info, false) 187 188 return ps, nil 189 } 190 191 func retrievePolicySpecification(c *Connector, name string) (*certificateTemplate, error) { 192 appName := policy.GetApplicationName(name) 193 if appName != "" { 194 c.zone.appName = appName 195 } else { 196 return nil, fmt.Errorf("application name is not valid, please provide a valid zone name in the format: appName\\CitName") 197 } 198 citName := policy.GetCitName(name) 199 if citName != "" { 200 c.zone.templateAlias = citName 201 } else { 202 return nil, fmt.Errorf("cit name is not valid, please provide a valid zone name in the format: appName\\CitName") 203 } 204 205 log.Println("Getting CIT") 206 cit, err := c.getTemplateByID() 207 208 if err != nil { 209 return nil, err 210 } 211 212 return cit, nil 213 214 } 215 216 func (c *Connector) getUsers() ([]string, error) { 217 var usersList []string 218 appDetails, _, err := c.getAppDetailsByName(c.zone.getApplicationName()) 219 if err != nil { 220 return nil, err 221 } 222 var teamsList *teams 223 for _, owner := range appDetails.OwnerIdType { 224 if owner.OwnerType == UserType.String() { 225 retrievedUser, userErr := c.retrieveUser(owner.OwnerId) 226 if userErr != nil { 227 return nil, userErr 228 } 229 usersList = append(usersList, retrievedUser.Username) 230 } else if owner.OwnerType == TeamType.String() { 231 if teamsList == nil { 232 teamsList, err = c.retrieveTeams() 233 if err != nil { 234 return nil, err 235 } 236 } 237 if teamsList != nil { 238 for _, t := range teamsList.Teams { 239 if t.ID == owner.OwnerId { 240 usersList = append(usersList, t.Name) 241 break 242 } 243 } 244 } 245 } 246 247 } 248 return usersList, nil 249 } 250 251 func PolicyExist(policyName string, c *Connector) (bool, error) { 252 c.zone.appName = policy.GetApplicationName(policyName) 253 citName := policy.GetCitName(policyName) 254 if citName != "" { 255 c.zone.templateAlias = citName 256 } else { 257 return false, fmt.Errorf("cit name is not valid, please provide a valid zone name in the format: appName\\CitName") 258 } 259 260 _, err := c.getTemplateByID() 261 return err == nil, nil 262 } 263 264 func (c *Connector) createApplication(appName string, ps *policy.PolicySpecification, cit *certificateTemplate) (*policy.Application, error) { 265 appIssuingTemplate := make(map[string]string) 266 appIssuingTemplate[cit.Name] = cit.ID 267 268 var owners []policy.OwnerIdType 269 var err error 270 var statusCode int 271 var status string 272 273 //if users are passed to the PS, resolve the related Owners to set them 274 if len(ps.Users) > 0 { 275 owners, err = c.resolveOwners(ps.Users) 276 } else { //if users are not specified in PS, then the current User should be used as owner 277 var owner *policy.OwnerIdType 278 owner, err = c.getOwnerFromUserDetails() 279 if owner != nil { 280 owners = []policy.OwnerIdType{*owner} 281 } 282 } 283 284 if err != nil { 285 return nil, fmt.Errorf("an error happened trying to resolve the owners: %w", err) 286 } 287 288 //create application 289 appReq := policy.Application{ 290 OwnerIdsAndTypes: owners, 291 Name: appName, 292 CertificateIssuingTemplateAliasIdMap: appIssuingTemplate, 293 } 294 295 url := c.getURL(urlAppRoot) 296 297 statusCode, status, _, err = c.request("POST", url, appReq) 298 if err != nil { 299 return nil, err 300 } 301 if statusCode != 201 { 302 return nil, fmt.Errorf("unexpected result %s attempting to create application %s", status, appName) 303 } 304 305 return &appReq, nil 306 } 307 308 func (c *Connector) updateApplication(name string, ps *policy.PolicySpecification, cit *certificateTemplate, appDetails *ApplicationDetails) error { 309 310 //creating the app to use as request 311 appReq := createAppUpdateRequest(appDetails) 312 313 //determining if the relationship between application and cit exist 314 citAddedToApp := false 315 exist, err := PolicyExist(name, c) 316 if err != nil { 317 return err 318 } 319 if !exist { 320 c.addCitToApp(&appReq, cit) 321 citAddedToApp = true 322 } 323 324 //determining if the owners where provided and should be updated 325 ownersUpdated := false 326 //given that the application exists, the only way to update the owners at the application 327 //is that users in the policy specification were provided 328 if len(ps.Users) > 0 { 329 //resolving and setting owners 330 owners, err := c.resolveOwners(ps.Users) 331 if err != nil { 332 return fmt.Errorf("an error happened trying to resolve the owners: %w", err) 333 } 334 appReq.OwnerIdsAndTypes = owners 335 ownersUpdated = true 336 } 337 338 //if the cit was added to the app or the owners were updated, then is required 339 //to update the application 340 if citAddedToApp || ownersUpdated { 341 url := c.getURL(urlAppRoot) 342 url = fmt.Sprint(url, "/", appDetails.ApplicationId) 343 _, _, _, err = c.request("PUT", url, appReq) 344 if err != nil { 345 return err 346 } 347 } 348 349 return nil 350 } 351 352 func (c *Connector) addCitToApp(app *policy.Application, cit *certificateTemplate) { 353 //add cit to the map. 354 value, ok := app.CertificateIssuingTemplateAliasIdMap[cit.Name] 355 if !ok || value != cit.ID { 356 app.CertificateIssuingTemplateAliasIdMap[cit.Name] = cit.ID 357 } 358 } 359 360 func (c *Connector) resolveOwners(usersList []string) ([]policy.OwnerIdType, error) { 361 362 var owners []policy.OwnerIdType 363 var teams *teams 364 var err error 365 366 for _, userName := range usersList { 367 //The error should be ignored in order to confirm if the userName is not a TeamName 368 users, _ := c.retrieveUsers(userName) 369 370 if users != nil { 371 owners = appendOwner(owners, users.Users[0].ID, UserType) 372 } else { 373 if teams == nil { 374 teams, err = c.retrieveTeams() 375 } 376 if err != nil { 377 return nil, err 378 } 379 if teams != nil { 380 var found = false 381 for _, team := range teams.Teams { 382 if team.Name == userName { 383 owners = appendOwner(owners, team.ID, TeamType) 384 found = true 385 break 386 } 387 } 388 if !found { 389 return nil, fmt.Errorf("it was not possible to find the user %s", userName) 390 } 391 } 392 } 393 } 394 395 return owners, err 396 } 397 398 func appendOwner(owners []policy.OwnerIdType, ownerId string, ownerType OwnerType) []policy.OwnerIdType { 399 owner := createOwner(ownerId, ownerType) 400 return append(owners, *owner) 401 } 402 403 func (c *Connector) getOwnerFromUserDetails() (*policy.OwnerIdType, error) { 404 userDetails, err := c.getUserDetails() 405 if err != nil { 406 return nil, err 407 } 408 owner := createOwner(userDetails.User.ID, UserType) 409 return owner, nil 410 } 411 412 func createOwner(ownerId string, ownerType OwnerType) *policy.OwnerIdType { 413 ownerIdType := policy.OwnerIdType{ 414 OwnerId: ownerId, 415 OwnerType: ownerType.String(), 416 } 417 418 return &ownerIdType 419 }