You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

275 lines
7.0 KiB

5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
4 years ago
4 years ago
4 years ago
5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. package s3api
  2. import (
  3. "fmt"
  4. "github.com/chrislusf/seaweedfs/weed/filer"
  5. "github.com/chrislusf/seaweedfs/weed/s3api/s3_constants"
  6. "io/ioutil"
  7. "net/http"
  8. "github.com/chrislusf/seaweedfs/weed/glog"
  9. "github.com/chrislusf/seaweedfs/weed/pb/iam_pb"
  10. xhttp "github.com/chrislusf/seaweedfs/weed/s3api/http"
  11. "github.com/chrislusf/seaweedfs/weed/s3api/s3err"
  12. )
  13. type Action string
  14. type Iam interface {
  15. Check(f http.HandlerFunc, actions ...Action) http.HandlerFunc
  16. }
  17. type IdentityAccessManagement struct {
  18. identities []*Identity
  19. domain string
  20. }
  21. type Identity struct {
  22. Name string
  23. Credentials []*Credential
  24. Actions []Action
  25. }
  26. type Credential struct {
  27. AccessKey string
  28. SecretKey string
  29. }
  30. func NewIdentityAccessManagement(option *S3ApiServerOption) *IdentityAccessManagement {
  31. iam := &IdentityAccessManagement{
  32. domain: option.DomainName,
  33. }
  34. if option.Config != "" {
  35. if err := iam.loadS3ApiConfigurationFromFile(option.Config); err != nil {
  36. glog.Fatalf("fail to load config file %s: %v", option.Config, err)
  37. }
  38. } else {
  39. if err := iam.loadS3ApiConfigurationFromFiler(option); err != nil {
  40. glog.Warningf("fail to load config: %v", err)
  41. }
  42. }
  43. return iam
  44. }
  45. func (iam *IdentityAccessManagement) loadS3ApiConfigurationFromFiler(option *S3ApiServerOption) error {
  46. content, err := filer.ReadContent(option.Filer, filer.IamConfigDirecotry, filer.IamIdentityFile)
  47. if err != nil {
  48. return fmt.Errorf("read S3 config: %v", err)
  49. }
  50. return iam.loadS3ApiConfigurationFromBytes(content)
  51. }
  52. func (iam *IdentityAccessManagement) loadS3ApiConfigurationFromFile(fileName string) error {
  53. content, readErr := ioutil.ReadFile(fileName)
  54. if readErr != nil {
  55. glog.Warningf("fail to read %s : %v", fileName, readErr)
  56. return fmt.Errorf("fail to read %s : %v", fileName, readErr)
  57. }
  58. return iam.loadS3ApiConfigurationFromBytes(content)
  59. }
  60. func (iam *IdentityAccessManagement) loadS3ApiConfigurationFromBytes(content []byte) error {
  61. s3ApiConfiguration := &iam_pb.S3ApiConfiguration{}
  62. if err := filer.ParseS3ConfigurationFromBytes(content, s3ApiConfiguration); err != nil {
  63. glog.Warningf("unmarshal error: %v", err)
  64. return fmt.Errorf("unmarshal error: %v", err)
  65. }
  66. if err := iam.loadS3ApiConfiguration(s3ApiConfiguration); err != nil {
  67. return err
  68. }
  69. return nil
  70. }
  71. func (iam *IdentityAccessManagement) loadS3ApiConfiguration(config *iam_pb.S3ApiConfiguration) error {
  72. var identities []*Identity
  73. for _, ident := range config.Identities {
  74. t := &Identity{
  75. Name: ident.Name,
  76. Credentials: nil,
  77. Actions: nil,
  78. }
  79. for _, action := range ident.Actions {
  80. t.Actions = append(t.Actions, Action(action))
  81. }
  82. for _, cred := range ident.Credentials {
  83. t.Credentials = append(t.Credentials, &Credential{
  84. AccessKey: cred.AccessKey,
  85. SecretKey: cred.SecretKey,
  86. })
  87. }
  88. identities = append(identities, t)
  89. }
  90. // atomically switch
  91. iam.identities = identities
  92. return nil
  93. }
  94. func (iam *IdentityAccessManagement) isEnabled() bool {
  95. return len(iam.identities) > 0
  96. }
  97. func (iam *IdentityAccessManagement) lookupByAccessKey(accessKey string) (identity *Identity, cred *Credential, found bool) {
  98. for _, ident := range iam.identities {
  99. for _, cred := range ident.Credentials {
  100. if cred.AccessKey == accessKey {
  101. return ident, cred, true
  102. }
  103. }
  104. }
  105. return nil, nil, false
  106. }
  107. func (iam *IdentityAccessManagement) lookupAnonymous() (identity *Identity, found bool) {
  108. for _, ident := range iam.identities {
  109. if ident.Name == "anonymous" {
  110. return ident, true
  111. }
  112. }
  113. return nil, false
  114. }
  115. func (iam *IdentityAccessManagement) Auth(f http.HandlerFunc, action Action) http.HandlerFunc {
  116. if !iam.isEnabled() {
  117. return f
  118. }
  119. return func(w http.ResponseWriter, r *http.Request) {
  120. identity, errCode := iam.authRequest(r, action)
  121. if errCode == s3err.ErrNone {
  122. if identity != nil && identity.Name != "" {
  123. r.Header.Set(xhttp.AmzIdentityId, identity.Name)
  124. if identity.isAdmin() {
  125. r.Header.Set(xhttp.AmzIsAdmin, "true")
  126. }
  127. }
  128. f(w, r)
  129. return
  130. }
  131. writeErrorResponse(w, errCode, r.URL)
  132. }
  133. }
  134. // check whether the request has valid access keys
  135. func (iam *IdentityAccessManagement) authRequest(r *http.Request, action Action) (*Identity, s3err.ErrorCode) {
  136. var identity *Identity
  137. var s3Err s3err.ErrorCode
  138. var found bool
  139. switch getRequestAuthType(r) {
  140. case authTypeStreamingSigned:
  141. return identity, s3err.ErrNone
  142. case authTypeUnknown:
  143. glog.V(3).Infof("unknown auth type")
  144. return identity, s3err.ErrAccessDenied
  145. case authTypePresignedV2, authTypeSignedV2:
  146. glog.V(3).Infof("v2 auth type")
  147. identity, s3Err = iam.isReqAuthenticatedV2(r)
  148. case authTypeSigned, authTypePresigned:
  149. glog.V(3).Infof("v4 auth type")
  150. identity, s3Err = iam.reqSignatureV4Verify(r)
  151. case authTypePostPolicy:
  152. glog.V(3).Infof("post policy auth type")
  153. return identity, s3err.ErrNone
  154. case authTypeJWT:
  155. glog.V(3).Infof("jwt auth type")
  156. return identity, s3err.ErrNotImplemented
  157. case authTypeAnonymous:
  158. identity, found = iam.lookupAnonymous()
  159. if !found {
  160. return identity, s3err.ErrAccessDenied
  161. }
  162. default:
  163. return identity, s3err.ErrNotImplemented
  164. }
  165. if s3Err != s3err.ErrNone {
  166. return identity, s3Err
  167. }
  168. glog.V(3).Infof("user name: %v actions: %v", identity.Name, identity.Actions)
  169. bucket, _ := getBucketAndObject(r)
  170. if !identity.canDo(action, bucket) {
  171. return identity, s3err.ErrAccessDenied
  172. }
  173. return identity, s3err.ErrNone
  174. }
  175. func (iam *IdentityAccessManagement) authUser(r *http.Request) (*Identity, s3err.ErrorCode) {
  176. var identity *Identity
  177. var s3Err s3err.ErrorCode
  178. var found bool
  179. switch getRequestAuthType(r) {
  180. case authTypeStreamingSigned:
  181. return identity, s3err.ErrNone
  182. case authTypeUnknown:
  183. glog.V(3).Infof("unknown auth type")
  184. return identity, s3err.ErrAccessDenied
  185. case authTypePresignedV2, authTypeSignedV2:
  186. glog.V(3).Infof("v2 auth type")
  187. identity, s3Err = iam.isReqAuthenticatedV2(r)
  188. case authTypeSigned, authTypePresigned:
  189. glog.V(3).Infof("v4 auth type")
  190. identity, s3Err = iam.reqSignatureV4Verify(r)
  191. case authTypePostPolicy:
  192. glog.V(3).Infof("post policy auth type")
  193. return identity, s3err.ErrNone
  194. case authTypeJWT:
  195. glog.V(3).Infof("jwt auth type")
  196. return identity, s3err.ErrNotImplemented
  197. case authTypeAnonymous:
  198. identity, found = iam.lookupAnonymous()
  199. if !found {
  200. return identity, s3err.ErrAccessDenied
  201. }
  202. default:
  203. return identity, s3err.ErrNotImplemented
  204. }
  205. glog.V(3).Infof("auth error: %v", s3Err)
  206. if s3Err != s3err.ErrNone {
  207. return identity, s3Err
  208. }
  209. return identity, s3err.ErrNone
  210. }
  211. func (identity *Identity) canDo(action Action, bucket string) bool {
  212. if identity.isAdmin() {
  213. return true
  214. }
  215. for _, a := range identity.Actions {
  216. if a == action {
  217. return true
  218. }
  219. }
  220. if bucket == "" {
  221. return false
  222. }
  223. limitedByBucket := string(action) + ":" + bucket
  224. adminLimitedByBucket := s3_constants.ACTION_ADMIN + ":" + bucket
  225. for _, a := range identity.Actions {
  226. if string(a) == limitedByBucket {
  227. return true
  228. }
  229. if string(a) == adminLimitedByBucket {
  230. return true
  231. }
  232. }
  233. return false
  234. }
  235. func (identity *Identity) isAdmin() bool {
  236. for _, a := range identity.Actions {
  237. if a == "Admin" {
  238. return true
  239. }
  240. }
  241. return false
  242. }