github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/model/permission/doctype.go (about) 1 package permission 2 3 import ( 4 "fmt" 5 "net/http" 6 "strings" 7 "unicode" 8 9 "github.com/cozy/cozy-stack/pkg/consts" 10 "github.com/labstack/echo/v4" 11 ) 12 13 var readable = true 14 var none = false 15 16 var blockList = map[string]bool{ 17 // Global databases 18 consts.Instances: none, 19 consts.AccountTypes: none, 20 consts.KonnectorsMaintenance: none, 21 consts.RemoteSecrets: none, 22 23 // Only stack can manipulate them 24 consts.Sessions: none, 25 consts.Permissions: none, 26 consts.Intents: none, 27 consts.OAuthClients: none, 28 consts.OAuthAccessCodes: none, 29 consts.Archives: none, 30 consts.Sharings: none, 31 consts.Shared: none, 32 consts.SoftDeletedAccounts: none, 33 34 // Synthetic doctypes (API only) 35 consts.CertifiedCarbonCopy: none, 36 consts.CertifiedElectronicSafe: none, 37 consts.DirSizes: none, 38 consts.TriggersState: none, 39 consts.SharingsAnswer: none, 40 consts.SharingsMoved: none, 41 consts.Support: none, 42 consts.BitwardenProfiles: none, 43 consts.OfficeURL: none, 44 consts.NotesURL: none, 45 consts.AppsOpenParameters: none, 46 47 // Synthetic doctypes (realtime events only) 48 consts.AuthConfirmations: none, 49 consts.JobEvents: none, 50 consts.SharingsInitialSync: none, 51 consts.NotesEvents: none, 52 consts.NotesTelepointers: none, 53 consts.Thumbnails: none, 54 consts.AppLogs: none, 55 56 // Only stack can write them 57 consts.Jobs: readable, 58 consts.Triggers: readable, 59 consts.Apps: readable, 60 consts.Konnectors: readable, 61 consts.Files: readable, 62 consts.FilesVersions: readable, 63 consts.Notifications: readable, 64 consts.RemoteRequests: readable, 65 consts.SessionsLogins: readable, 66 consts.NotesSteps: readable, 67 consts.NotesImages: readable, 68 consts.BitwardenContacts: readable, 69 } 70 71 // CheckReadable will abort the context and returns false if the doctype 72 // is unreadable 73 func CheckReadable(doctype string) error { 74 if err := CheckDoctypeName(doctype, false); err != nil { 75 return err 76 } 77 78 readable, inblocklist := blockList[doctype] 79 if !inblocklist || readable { 80 return nil 81 } 82 83 return &echo.HTTPError{ 84 Code: http.StatusForbidden, 85 Message: fmt.Sprintf("reserved doctype %s unreadable", doctype), 86 } 87 } 88 89 // CheckWritable will abort the echo context if the doctype 90 // is unwritable 91 func CheckWritable(doctype string) error { 92 if err := CheckDoctypeName(doctype, false); err != nil { 93 return err 94 } 95 96 _, inblocklist := blockList[doctype] 97 if !inblocklist { 98 return nil 99 } 100 101 return &echo.HTTPError{ 102 Code: http.StatusForbidden, 103 Message: fmt.Sprintf("reserved doctype %s unwritable", doctype), 104 } 105 } 106 107 // CheckDoctypeName will return an error if the doctype name is invalid. 108 // A doctype name must be composed of lowercase letters, digits, . and _ 109 // characters to be valid. 110 func CheckDoctypeName(doctype string, authorizeWildcard bool) error { 111 err := &echo.HTTPError{ 112 Code: http.StatusForbidden, 113 Message: fmt.Sprintf("%s is not a valid doctype name", doctype), 114 } 115 116 if len(doctype) == 0 { 117 return err 118 } 119 120 if authorizeWildcard && isWildcard(doctype) { 121 // Wildcards on too large domains are not allowed 122 if strings.Count(doctype, ".") < 3 { 123 return err 124 } 125 doctype = TrimWildcard(doctype) 126 } 127 128 for _, c := range doctype { 129 if unicode.IsLower(c) || unicode.IsDigit(c) || c == '.' || c == '_' { 130 continue 131 } 132 return err 133 } 134 135 // A dot at the beginning or the end of the doctype name is not allowed 136 if doctype[0] == '.' || doctype[len(doctype)-1] == '.' { 137 return err 138 } 139 // Two dots side-by-side are not allowed 140 if strings.Contains(doctype, "..") { 141 return err 142 } 143 144 return nil 145 } 146 147 const allDocTypes = "*" 148 const wildcardSuffix = ".*" 149 150 func isMaximal(doctype string) bool { 151 return doctype == allDocTypes 152 } 153 154 func isWildcard(doctype string) bool { 155 return strings.HasSuffix(doctype, wildcardSuffix) 156 } 157 158 // TrimWildcard returns the given doctype without the wildcard suffix 159 func TrimWildcard(doctype string) string { 160 return strings.TrimSuffix(doctype, wildcardSuffix) 161 }