github.com/osievert/jfrog-cli-core@v1.2.7/artifactory/commands/permissiontarget/template.go (about) 1 package permissiontarget 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "github.com/c-bata/go-prompt" 7 "github.com/jfrog/jfrog-cli-core/artifactory/commands/utils" 8 "github.com/jfrog/jfrog-cli-core/utils/config" 9 "github.com/jfrog/jfrog-client-go/utils/errorutils" 10 "github.com/jfrog/jfrog-client-go/utils/log" 11 "io/ioutil" 12 "sort" 13 "strings" 14 ) 15 16 type PermissionTargetTemplateCommand struct { 17 path string 18 } 19 20 const ( 21 // Strings for prompt questions 22 SelectPermissionTargetSectionMsg = "Select the permission target section to configure" + utils.PressTabMsg 23 LeaveEmptyForDefault = "(press enter for default)" 24 25 // Yes,No answers 26 Yes = "yes" 27 No = "no" 28 29 // Main permission target configuration JSON keys 30 Name = "name" 31 Repo = "repo" 32 Build = "build" 33 ReleaseBundle = "releaseBundle" 34 35 IncludePatternsDefault = "**" 36 ExcludePatternsDefault = "" 37 38 // Possible permissions 39 read = "read" 40 write = "write" 41 annotate = "annotate" 42 delete = "delete" 43 manage = "manage" 44 managedXrayMeta = "managedXrayMeta" 45 distribute = "distribute" 46 47 permissionSelectEnd = utils.DummyDefaultAnswer 48 ) 49 50 func NewPermissionTargetTemplateCommand() *PermissionTargetTemplateCommand { 51 return &PermissionTargetTemplateCommand{} 52 } 53 54 func (pttc *PermissionTargetTemplateCommand) SetTemplatePath(path string) *PermissionTargetTemplateCommand { 55 pttc.path = path 56 return pttc 57 } 58 59 func (pttc *PermissionTargetTemplateCommand) RtDetails() (*config.ArtifactoryDetails, error) { 60 // Since it's a local command, usage won't be reported. 61 return nil, nil 62 } 63 64 func (pttc *PermissionTargetTemplateCommand) Run() (err error) { 65 err = utils.ValidateTemplatePath(pttc.path) 66 if err != nil { 67 return 68 } 69 permissionTargetTemplateQuestionnaire := &utils.InteractiveQuestionnaire{ 70 MandatoryQuestionsKeys: []string{Name}, 71 QuestionsMap: questionMap, 72 OptionalKeysSuggests: optionalSuggestsMap, 73 } 74 err = permissionTargetTemplateQuestionnaire.Perform() 75 if err != nil { 76 return err 77 } 78 resBytes, err := json.Marshal(permissionTargetTemplateQuestionnaire.AnswersMap) 79 if err != nil { 80 return errorutils.CheckError(err) 81 } 82 if err = ioutil.WriteFile(pttc.path, resBytes, 0644); err != nil { 83 return errorutils.CheckError(err) 84 } 85 log.Info(fmt.Sprintf("Permission target configuration template successfully created at %s.", pttc.path)) 86 87 return nil 88 } 89 90 func (pttc *PermissionTargetTemplateCommand) CommandName() string { 91 return "rt_permission_target_template" 92 } 93 94 var optionalSuggestsMap = []prompt.Suggest{ 95 {Text: utils.SaveAndExit}, 96 {Text: Repo}, 97 {Text: Build}, 98 {Text: ReleaseBundle}, 99 } 100 101 // Each permission target section (repo/build/releaseBundle) can have the following keys: 102 // * repos - Mandatory for repo and releaseBundle. Has a const default value for build. 103 // * include/exclude-patterns - Optional, has a default value. 104 // * actions - Optional,includes two maps (users and groups): user/group name -> permissions array. 105 func permissionSectionCallBack(iq *utils.InteractiveQuestionnaire, section string) (value string, err error) { 106 if section == utils.SaveAndExit { 107 return 108 } 109 var sectionAnswer PermissionSectionAnswer 110 if section != Build { 111 sectionAnswer.Repositories = utils.AskString(reposQuestionInfo.Msg, reposQuestionInfo.PromptPrefix, false, reposQuestionInfo.AllowVars) 112 } 113 sectionAnswer.IncludePatterns = utils.AskStringWithDefault(includePatternsQuestionInfo.Msg, includePatternsQuestionInfo.PromptPrefix, IncludePatternsDefault) 114 sectionAnswer.ExcludePatterns = utils.AskString(excludePatternsQuestionInfo.Msg, excludePatternsQuestionInfo.PromptPrefix, true, excludePatternsQuestionInfo.AllowVars) 115 configureActions := utils.AskFromList("", configureActionsQuestionInfo.PromptPrefix+"users?"+utils.PressTabMsg, false, configureActionsQuestionInfo.Options, Yes) 116 if configureActions == Yes { 117 sectionAnswer.ActionsUsers = make(map[string]string) 118 readActionsMap("user", sectionAnswer.ActionsUsers) 119 } 120 configureActions = utils.AskFromList("", configureActionsQuestionInfo.PromptPrefix+"groups?", false, configureActionsQuestionInfo.Options, Yes) 121 if configureActions == Yes { 122 sectionAnswer.ActionsGroups = make(map[string]string) 123 readActionsMap("group", sectionAnswer.ActionsGroups) 124 } 125 iq.AnswersMap[section] = sectionAnswer 126 return 127 } 128 129 // We will read (user/group name, permissions) pairs until empty name is read. 130 func readActionsMap(actionsType string, actionsMap map[string]string) { 131 customKeyPrompt := "Insert " + actionsType + " name (press enter to finish) >" 132 for { 133 key := utils.AskString("", customKeyPrompt, true, false) 134 if key == "" { 135 return 136 } 137 value := strings.Join(readPermissionList(key), ",") 138 actionsMap[key] = value 139 } 140 } 141 142 func readPermissionList(permissionsOwner string) (permissions []string) { 143 var permissionsMap = map[string]bool{ 144 read: false, 145 write: false, 146 annotate: false, 147 delete: false, 148 manage: false, 149 managedXrayMeta: false, 150 distribute: false, 151 } 152 for { 153 answer := utils.AskFromList("", "Select permission value for "+permissionsOwner+" (press tab for options or enter to finish) >", true, buildPermissionSuggestArray(permissionsMap), permissionSelectEnd) 154 if answer == permissionSelectEnd { 155 break 156 } 157 // If the answer is a valid key, we will mark it with true to remove it from the suggestion list 158 if _, ok := permissionsMap[answer]; ok { 159 permissionsMap[answer] = true 160 } else { 161 // answer is a var, we will add it to the final permissions slice result 162 permissions = append(permissions, answer) 163 } 164 } 165 for key, value := range permissionsMap { 166 if value { 167 permissions = append(permissions, key) 168 } 169 } 170 return 171 } 172 173 func buildPermissionSuggestArray(permissionMap map[string]bool) (permissions []prompt.Suggest) { 174 for key, value := range permissionMap { 175 if !value { 176 permissions = append(permissions, prompt.Suggest{Text: key}) 177 } 178 } 179 sort.Slice(permissions, func(i, j int) bool { 180 return permissions[i].Text < permissions[j].Text 181 }) 182 return 183 } 184 185 var questionMap = map[string]utils.QuestionInfo{ 186 Name: { 187 Msg: "", 188 PromptPrefix: "Insert the permission target name >", 189 AllowVars: false, 190 Writer: utils.WriteStringAnswer, 191 MapKey: Name, 192 Callback: nil, 193 }, 194 utils.OptionalKey: { 195 Msg: "", 196 PromptPrefix: SelectPermissionTargetSectionMsg, 197 AllowVars: false, 198 Writer: nil, 199 MapKey: "", 200 Callback: permissionSectionCallBack, 201 }, 202 Repo: utils.FreeStringQuestionInfo, 203 Build: utils.FreeStringQuestionInfo, 204 ReleaseBundle: utils.FreeStringQuestionInfo, 205 } 206 207 var reposQuestionInfo = utils.QuestionInfo{ 208 Msg: "Insert the section's repositories value.\nYou can specify the name \"ANY\" to apply to all repositories, \"ANY REMOTE\" for all remote repositories or \"ANY LOCAL\" for all local repositories", 209 PromptPrefix: utils.CommaSeparatedListMsg + " >", 210 } 211 212 var includePatternsQuestionInfo = utils.QuestionInfo{ 213 Msg: "Insert a value for include-patterns", 214 PromptPrefix: utils.CommaSeparatedListMsg + " " + LeaveEmptyForDefault, 215 } 216 217 var excludePatternsQuestionInfo = utils.QuestionInfo{ 218 Msg: "Insert value for exclude-patterns", 219 PromptPrefix: utils.CommaSeparatedListMsg + " " + LeaveEmptyForDefault + " []:", 220 } 221 222 var configureActionsQuestionInfo = utils.QuestionInfo{ 223 PromptPrefix: "Configure actions for ", 224 Options: []prompt.Suggest{ 225 {Text: Yes}, 226 {Text: No}, 227 }, 228 } 229 230 type PermissionSectionAnswer struct { 231 Repositories string `json:"repositories,omitempty"` 232 IncludePatterns string `json:"include-patterns,omitempty"` 233 ExcludePatterns string `json:"exclude-patterns,omitempty"` 234 ActionsUsers map[string]string `json:"actions-users,omitempty"` 235 ActionsGroups map[string]string `json:"actions-groups,omitempty"` 236 }