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.

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