namevalidator.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package db
  15. import (
  16. "context"
  17. "fmt"
  18. "regexp"
  19. "strings"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/util/seclib"
  23. "yunion.io/x/sqlchemy"
  24. "yunion.io/x/onecloud/pkg/cloudcommon/consts"
  25. "yunion.io/x/onecloud/pkg/httperrors"
  26. "yunion.io/x/onecloud/pkg/mcclient"
  27. "yunion.io/x/onecloud/pkg/util/stringutils2"
  28. )
  29. func isNameUnique(ctx context.Context, manager IModelManager, ownerId mcclient.IIdentityProvider, name string, uniqValues jsonutils.JSONObject) (bool, error) {
  30. return isRawNameUnique(ctx, manager, ownerId, name, uniqValues, false)
  31. }
  32. func isRawNameUnique(ctx context.Context, manager IModelManager, ownerId mcclient.IIdentityProvider, name string, uniqValues jsonutils.JSONObject, isRaw bool) (bool, error) {
  33. var q *sqlchemy.SQuery
  34. if isRaw {
  35. q = manager.TableSpec().Instance().Query()
  36. } else {
  37. q = manager.Query()
  38. }
  39. q = manager.FilterByName(q, name)
  40. q = manager.FilterByOwner(ctx, q, manager, nil, ownerId, manager.NamespaceScope())
  41. if !isRaw {
  42. q = manager.FilterBySystemAttributes(q, nil, nil, manager.ResourceScope())
  43. if uniqValues != nil {
  44. q = manager.FilterByUniqValues(q, uniqValues)
  45. }
  46. }
  47. cnt, err := q.CountWithError()
  48. if err != nil {
  49. return false, err
  50. }
  51. return cnt == 0, nil
  52. }
  53. const forbiddenNameChars = "/\\;\n\r\t"
  54. func NewNameValidator(ctx context.Context, manager IModelManager, ownerId mcclient.IIdentityProvider, name string, uniqValues jsonutils.JSONObject) error {
  55. err := manager.ValidateName(name)
  56. if err != nil {
  57. return err
  58. }
  59. if strings.ContainsAny(name, forbiddenNameChars) {
  60. return errors.Wrapf(errors.ErrInvalidFormat, "name should not contains any of %q", forbiddenNameChars)
  61. }
  62. uniq, err := isNameUnique(ctx, manager, ownerId, name, uniqValues)
  63. if err != nil {
  64. return err
  65. }
  66. if !uniq {
  67. return httperrors.NewDuplicateNameError(manager.Keyword(), name)
  68. }
  69. return nil
  70. }
  71. func isAlterNameUnique(ctx context.Context, model IModel, name string) (bool, error) {
  72. return isRawAlterNameUnique(ctx, model, name, false)
  73. }
  74. func isRawAlterNameUnique(ctx context.Context, model IModel, name string, isRaw bool) (bool, error) {
  75. manager := model.GetModelManager()
  76. var q *sqlchemy.SQuery
  77. if isRaw {
  78. q = manager.TableSpec().Instance().Query()
  79. } else {
  80. q = manager.Query()
  81. }
  82. q = manager.FilterByName(q, name)
  83. q = manager.FilterByOwner(ctx, q, manager, nil, model.GetOwnerId(), manager.NamespaceScope())
  84. q = manager.FilterByNotId(q, model.GetId())
  85. if !isRaw {
  86. q = manager.FilterBySystemAttributes(q, nil, nil, manager.ResourceScope())
  87. if uniqValues := model.GetUniqValues(); uniqValues != nil {
  88. q = manager.FilterByUniqValues(q, uniqValues)
  89. }
  90. }
  91. cnt, err := q.CountWithError()
  92. if err != nil {
  93. return false, err
  94. }
  95. return cnt == 0, nil
  96. }
  97. func alterNameValidator(ctx context.Context, model IModel, name string) error {
  98. err := model.GetModelManager().ValidateName(name)
  99. if err != nil {
  100. return err
  101. }
  102. if strings.ContainsAny(name, forbiddenNameChars) {
  103. return errors.Wrapf(errors.ErrInvalidFormat, "name should not contains any of %q", forbiddenNameChars)
  104. }
  105. uniq, err := isAlterNameUnique(ctx, model, name)
  106. if err != nil {
  107. return err
  108. }
  109. if !uniq {
  110. return httperrors.NewDuplicateNameError("name", name)
  111. }
  112. return nil
  113. }
  114. func GenerateName(ctx context.Context, manager IModelManager, ownerId mcclient.IIdentityProvider, hint string) (string, error) {
  115. return GenerateName2(ctx, manager, ownerId, hint, nil, 1)
  116. }
  117. func GenerateAlterName(model IModel, hint string) (string, error) {
  118. if hint == model.GetName() {
  119. return hint, nil
  120. }
  121. return GenerateName2(nil, nil, nil, hint, model, 1)
  122. }
  123. func GenerateName2(ctx context.Context, manager IModelManager, ownerId mcclient.IIdentityProvider, hint string, model IModel, baseIndex int) (string, error) {
  124. _, pattern, patternLen, offset, ch := stringutils2.ParseNamePattern2(hint)
  125. var name string
  126. if patternLen == 0 {
  127. name = hint
  128. } else {
  129. if offset > 0 {
  130. baseIndex = offset
  131. }
  132. switch ch {
  133. case stringutils2.RandomChar:
  134. name = fmt.Sprintf(pattern, strings.ToLower(seclib.RandomPassword(patternLen)))
  135. case stringutils2.RepChar:
  136. fallthrough
  137. default:
  138. name = fmt.Sprintf(pattern, baseIndex)
  139. baseIndex += 1
  140. }
  141. }
  142. for {
  143. var uniq bool
  144. var err error
  145. if model == nil {
  146. uniq, err = isRawNameUnique(ctx, manager, ownerId, name, nil, consts.IsHistoricalUniqueName())
  147. } else {
  148. uniq, err = isRawAlterNameUnique(ctx, model, name, consts.IsHistoricalUniqueName())
  149. }
  150. if err != nil {
  151. return "", err
  152. }
  153. if uniq {
  154. return name, nil
  155. }
  156. switch ch {
  157. case stringutils2.RandomChar:
  158. name = fmt.Sprintf(pattern, strings.ToLower(seclib.RandomPassword(patternLen)))
  159. case stringutils2.RepChar:
  160. fallthrough
  161. default:
  162. name = fmt.Sprintf(pattern, baseIndex)
  163. baseIndex += 1
  164. }
  165. }
  166. }
  167. var (
  168. dnsNameREG = regexp.MustCompile(`^[a-z][a-z0-9-]*$`)
  169. )
  170. type SDnsNameValidatorManager struct{}
  171. func (manager *SDnsNameValidatorManager) ValidateName(name string) error {
  172. if dnsNameREG.MatchString(name) {
  173. return nil
  174. }
  175. return httperrors.NewInputParameterError("name starts with letter, and contains letter, number and - only")
  176. }
  177. var (
  178. hostNameREG = regexp.MustCompile(`^[a-z$][a-z0-9-${}.]*$`)
  179. )
  180. type SHostNameValidatorManager struct{}
  181. func (manager *SHostNameValidatorManager) ValidateName(name string) error {
  182. if hostNameREG.MatchString(name) {
  183. return nil
  184. }
  185. return httperrors.NewInputParameterError("name starts with letter, and contains letter, number and - only")
  186. }