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  }