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