// pathRoleWrite [go]
func (b *backend) pathRoleWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	roleName := d.Get("role").(string)
	if roleName == "" {
		return logical.ErrorResponse("missing role name"), nil
	}

	// Allowed users is an optional field, applicable for both OTP and Dynamic types.
	allowedUsers := d.Get("allowed_users").(string)

	// Validate the CIDR blocks
	cidrList := d.Get("cidr_list").(string)
	if cidrList != "" {
		valid, err := cidrutil.ValidateCIDRListString(cidrList, ",")
		if err != nil {
			return nil, fmt.Errorf("failed to validate cidr_list: %w", err)
		}
		if !valid {
			return logical.ErrorResponse("failed to validate cidr_list"), nil
		}
	}

	// Validate the excluded CIDR blocks
	excludeCidrList := d.Get("exclude_cidr_list").(string)
	if excludeCidrList != "" {
		valid, err := cidrutil.ValidateCIDRListString(excludeCidrList, ",")
		if err != nil {
			return nil, fmt.Errorf("failed to validate exclude_cidr_list entry: %w", err)
		}
		if !valid {
			return logical.ErrorResponse(fmt.Sprintf("failed to validate exclude_cidr_list entry: %v", err)), nil
		}
	}

	port := d.Get("port").(int)
	if port == 0 {
		port = 22
	}

	keyType := d.Get("key_type").(string)
	if keyType == "" {
		return logical.ErrorResponse("missing key type"), nil
	}
	keyType = strings.ToLower(keyType)

	var roleEntry sshRole
	if keyType == KeyTypeOTP {
		defaultUser := d.Get("default_user").(string)
		if defaultUser == "" {
			return logical.ErrorResponse("missing default user"), nil
		}

		// Admin user is not used if OTP key type is used because there is
		// no need to login to remote machine.
		adminUser := d.Get("admin_user").(string)
		if adminUser != "" {
			return logical.ErrorResponse("admin user not required for OTP type"), nil
		}

		// Below are the only fields used from the role structure for OTP type.
		roleEntry = sshRole{
			DefaultUser:     defaultUser,
			CIDRList:        cidrList,
			ExcludeCIDRList: excludeCidrList,
			KeyType:         KeyTypeOTP,
			Port:            port,
			AllowedUsers:    allowedUsers,
		}
	} else if keyType == KeyTypeDynamic {
		defaultUser := d.Get("default_user").(string)
		if defaultUser == "" {
			return logical.ErrorResponse("missing default user"), nil
		}
		// Key name is required by dynamic type and not by OTP type.
		keyName := d.Get("key").(string)
		if keyName == "" {
			return logical.ErrorResponse("missing key name"), nil
		}
		keyEntry, err := req.Storage.Get(ctx, fmt.Sprintf("keys/%s", keyName))
		if err != nil || keyEntry == nil {
			return logical.ErrorResponse(fmt.Sprintf("invalid 'key': %q", keyName)), nil
		}

		installScript := d.Get("install_script").(string)
		keyOptionSpecs := d.Get("key_option_specs").(string)

		// Setting the default script here. The script will install the
		// generated public key in the authorized_keys file of linux host.
		if installScript == "" {
			installScript = DefaultPublicKeyInstallScript
		}

		adminUser := d.Get("admin_user").(string)
		if adminUser == "" {
			return logical.ErrorResponse("missing admin username"), nil
		}

		// This defaults to 2048, but it can also be 1024, 3072, 4096, or 8192.
		// In the near future, we should disallow 1024-bit SSH keys.
		keyBits := d.Get("key_bits").(int)
		if keyBits == 0 {
			keyBits = 2048
		}
		if keyBits != 1024 && keyBits != 2048 && keyBits != 3072 && keyBits != 4096 && keyBits != 8192 {
			return logical.ErrorResponse("invalid key_bits field"), nil
		}

		// Store all the fields required by dynamic key type
		roleEntry = sshRole{
			KeyName:         keyName,
			AdminUser:       adminUser,
			DefaultUser:     defaultUser,
			CIDRList:        cidrList,
			ExcludeCIDRList: excludeCidrList,
			Port:            port,
			KeyType:         KeyTypeDynamic,
			KeyBits:         keyBits,
			InstallScript:   installScript,
			AllowedUsers:    allowedUsers,
			KeyOptionSpecs:  keyOptionSpecs,
		}
	} else if keyType == KeyTypeCA {
		algorithmSigner := ""
		algorithmSignerRaw, ok := d.GetOk("algorithm_signer")
		if ok {
			algorithmSigner = algorithmSignerRaw.(string)
			switch algorithmSigner {
			case ssh.SigAlgoRSA, ssh.SigAlgoRSASHA2256, ssh.SigAlgoRSASHA2512:
			case "":
				// This case is valid, and the sign operation will use the signer's
				// default algorithm.
			default:
				return nil, fmt.Errorf("unknown algorithm signer %q", algorithmSigner)
			}
		}

		role, errorResponse := b.createCARole(allowedUsers, d.Get("default_user").(string), algorithmSigner, d)
		if errorResponse != nil {
			return errorResponse, nil
		}
		roleEntry = *role
	} else {
		return logical.ErrorResponse("invalid key type"), nil
	}

	entry, err := logical.StorageEntryJSON(fmt.Sprintf("roles/%s", roleName), roleEntry)
	if err != nil {
		return nil, err
	}

	if err := req.Storage.Put(ctx, entry); err != nil {
		return nil, err
	}
	return nil, nil
}