-
Notifications
You must be signed in to change notification settings - Fork 186
/
Copy pathaccount_resolver.go
126 lines (106 loc) · 3.97 KB
/
account_resolver.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package middleware
import (
"errors"
"net/http"
"github.com/cs3org/reva/pkg/auth/scope"
"github.com/owncloud/ocis/proxy/pkg/user/backend"
tokenPkg "github.com/cs3org/reva/pkg/token"
"github.com/cs3org/reva/pkg/token/manager/jwt"
revauser "github.com/cs3org/reva/pkg/user"
"github.com/owncloud/ocis/ocis-pkg/log"
"github.com/owncloud/ocis/ocis-pkg/oidc"
)
// AccountResolver provides a middleware which mints a jwt and adds it to the proxied request based
// on the oidc-claims
func AccountResolver(optionSetters ...Option) func(next http.Handler) http.Handler {
options := newOptions(optionSetters...)
logger := options.Logger
return func(next http.Handler) http.Handler {
tokenManager, err := jwt.New(map[string]interface{}{
"secret": options.TokenManagerConfig.JWTSecret,
"expires": int64(60),
})
if err != nil {
logger.Fatal().Err(err).Msg("Could not initialize token-manager")
}
return &accountResolver{
next: next,
logger: logger,
tokenManager: tokenManager,
userProvider: options.UserProvider,
userOIDCClaim: options.UserOIDCClaim,
userCS3Claim: options.UserCS3Claim,
autoProvisionAccounts: options.AutoprovisionAccounts,
}
}
}
type accountResolver struct {
next http.Handler
logger log.Logger
tokenManager tokenPkg.Manager
userProvider backend.UserBackend
autoProvisionAccounts bool
userOIDCClaim string
userCS3Claim string
}
// TODO do not use the context to store values: https://medium.com/@cep21/how-to-correctly-use-context-context-in-go-1-7-8f2c0fafdf39
func (m accountResolver) ServeHTTP(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
claims := oidc.FromContext(ctx)
u, ok := revauser.ContextGetUser(ctx)
// TODO what if an X-Access-Token is set? happens eg for download requests to the /data endpoint in the reva frontend
if claims == nil && !ok {
m.next.ServeHTTP(w, req)
return
}
if u == nil && claims != nil {
var err error
var value string
var ok bool
if value, ok = claims[m.userOIDCClaim].(string); !ok || value == "" {
m.logger.Error().Str("claim", m.userOIDCClaim).Interface("claims", claims).Msg("claim not set or empty")
w.WriteHeader(http.StatusInternalServerError) // admin needs to make the idp send the right claim
return
}
u, err = m.userProvider.GetUserByClaims(req.Context(), m.userCS3Claim, value, true)
if errors.Is(err, backend.ErrAccountNotFound) {
m.logger.Debug().Str("claim", m.userOIDCClaim).Str("value", value).Msg("User by claim not found")
if !m.autoProvisionAccounts {
m.logger.Debug().Interface("claims", claims).Msg("Autoprovisioning disabled")
w.WriteHeader(http.StatusUnauthorized)
return
}
m.logger.Debug().Interface("claims", claims).Msg("Autoprovisioning user")
u, err = m.userProvider.CreateUserFromClaims(req.Context(), claims)
// TODO instead of creating an account create a personal storage via the CS3 admin api?
// see https://cs3org.github.io/cs3apis/#cs3.admin.user.v1beta1.CreateUserRequest
}
if errors.Is(err, backend.ErrAccountDisabled) {
m.logger.Debug().Interface("claims", claims).Msg("Disabled")
w.WriteHeader(http.StatusUnauthorized)
return
}
if err != nil {
m.logger.Error().Err(err).Msg("Could not get user by claim")
w.WriteHeader(http.StatusInternalServerError)
return
}
// add user to context for selectors
ctx = revauser.ContextSetUser(ctx, u)
req = req.WithContext(ctx)
m.logger.Debug().Interface("claims", claims).Interface("user", u).Msg("associated claims with user")
}
s, err := scope.AddOwnerScope(nil)
if err != nil {
m.logger.Error().Err(err).Msg("could not get owner scope")
return
}
token, err := m.tokenManager.MintToken(ctx, u, s)
if err != nil {
m.logger.Error().Err(err).Msg("could not mint token")
w.WriteHeader(http.StatusInternalServerError)
return
}
req.Header.Set(tokenPkg.TokenHeader, token)
m.next.ServeHTTP(w, req)
}