Repository navigation
feat(guestlinks)!: auth-guest service - #3609
Conversation
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 253 |
🟢 Coverage 29.25% diff coverage · +0.08% coverage variation
Metric Results Coverage variation ✅ +0.08% coverage variation (-1.00%) Diff coverage ✅ 29.25% diff coverage Coverage variation details
Coverable lines Covered lines Coverage Common ancestor commit (8e51430) 89530 21781 24.33% Head commit (aeb11b0) 90725 (+1195) 22143 (+362) 24.41% (+0.08%) Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch:
<coverage of head commit> - <coverage of common ancestor commit>Diff coverage details
Coverable lines Covered lines Diff coverage Pull request (#3609) 1200 351 29.25% Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified:
<covered lines added or modified>/<coverable lines added or modified> * 100%
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
aduffeck
left a comment
There was a problem hiding this comment.
Just a few things to refine. Please also add license headers to the new files.
|
|
||
| type FileStorage struct { | ||
| root string | ||
| mu sync.Mutex |
There was a problem hiding this comment.
To make the service scalable (on a RWX storage) we should use actual posix file locks instead of a Mutex here I think.
|
|
||
| type Token struct { | ||
| ShareIDHash string | ||
| SecretHash string |
There was a problem hiding this comment.
Why do we need the SecretHash field? it's always simply a sha256sum of the secret, so it looks redundant to me. Also the candidate.SecretHash != secretHash check in line 65 can never be false.
I think I'd rather replace it with a SecretHash() or HashedSecret() function which returns a (cached?) hash of the secret.
| Sharer *user.UserId | ||
| ItemID *provider.ResourceId | ||
| ResourceName string | ||
| Token string |
There was a problem hiding this comment.
This event should include the grantee's email adress so the notification service doesn't have to look up the share anymore.
| } | ||
|
|
||
| func (s *FileStorage) path(shareIDHash string) string { | ||
| return filepath.Join(s.root, shareIDHash[:2], shareIDHash[2:4], shareIDHash[4:]+".json") |
There was a problem hiding this comment.
This could panic when verifying a malformed token and we need to be careful with path traversials.
Maybe filepath.Clean() plus verifying the length of the segments would do the trick?
| Redeemed bool `json:"redeemed"` | ||
| } | ||
|
|
||
| type Storage interface { |
There was a problem hiding this comment.
Could we name this differently? The implementations don't just store, they also include business logic (redeeming tokens now, probably thins like adding pin verification in the future), so maybe something like Backend or similar would be more appropriate.
There was a problem hiding this comment.
Not sure if this is better, but in reva these kinds of interfaces are called Manager I think.
| // Validate validates the config | ||
| func Validate(cfg *config.Config) error { | ||
| if cfg.TokenManager == nil || cfg.TokenManager.JWTSecret == "" { | ||
| return shared.MissingJWTTokenError(cfg.Service.Name) |
There was a problem hiding this comment.
If I'm not mistaken this will break existing installations? We'll have to make sure to bump the version and write according release notes when merging this.
| ShareID: shareID, | ||
| ShareIDHash: tok.ShareIDHash, | ||
| SecretHash: tok.SecretHash, | ||
| Expiry: utils.TSToTime(share.GetExpiration()), |
There was a problem hiding this comment.
The token expiry shouldn't be the share expiration date but time.Now().Add(30 * time.Minute) according to #2513
| log.Debug().Err(err).Msg("token already redeemed") | ||
| w.WriteHeader(http.StatusConflict) | ||
| case errors.Is(err, guestauth.ErrExpired): | ||
| log.Debug().Err(err).Msg("token expired") |
There was a problem hiding this comment.
The errors should follow what was discussed in #2517 (comment) and e.g. return a 401 with something like
{
"error_type": "token_expired",
"message": "Your token has expired.",
"share_id": "<the-share-id>",
}
in the body here.
|
|
||
| Service Service `yaml:"-"` | ||
|
|
||
| LogLevel string `yaml:"loglevel" env:"OC_LOG_LEVEL;GUESTAUTH_LOG_LEVEL" desc:"The log level. Valid values are: 'panic', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'." introductionVersion:"1.0.0"` |
There was a problem hiding this comment.
| LogLevel string `yaml:"loglevel" env:"OC_LOG_LEVEL;GUESTAUTH_LOG_LEVEL" desc:"The log level. Valid values are: 'panic', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'." introductionVersion:"1.0.0"` | |
| LogLevel string `yaml:"loglevel" env:"OC_LOG_LEVEL;GUESTAUTH_LOG_LEVEL" desc:"The log level. Valid values are: 'panic', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'." introductionVersion:"%%NEXT%%"` |
There was a problem hiding this comment.
Same for all the other new env vars that are being introduced here.
| defer s.mu.Unlock() | ||
|
|
||
| p := s.path(shareIDHash) | ||
| if _, err := os.Stat(p); err != nil { |
There was a problem hiding this comment.
Maybe handle not exists from os.Remove() instead of the stat?
|
I would suggest that the new service is named |
|
@aduffeck about the license headers, I found just this one: should it be used or there is somewhere some other header? also to which files it should be applied? all .go files from the service? |
Right, this one to all new source files we add. |
e8ce608 to
3ffdb78
Compare
| @@ -74,13 +97,30 @@ func (s *FileManager) Redeem(shareIDHash string) error { | |||
| return s.add(rec) | |||
| } | |||
|
|
|||
| func (s *FileManager) lockStore() (*flock.Flock, error) { | |||
There was a problem hiding this comment.
Could we just lock the particular record that's being mutated instead of the global store lock?
| cfg.Groups.Commons = cfg.Commons | ||
| return groups.Execute(cfg.Groups) | ||
| }) | ||
| if opts.Config.Commons != nil && opts.Config.Commons.EnableGuestLinks { |
There was a problem hiding this comment.
This doesn't currently seem to work. The opts.Config.Commons struct is still nil here (unless the yaml config file has a section named shared).
AFAICS this is a bug in the top-level config parser ("pkg/config/parser/parse.go") which does not initialize cfg.Commons in EnsureDefaults().
72774b6 to
456c8ae
Compare
|
Could we maybe add the diagram (the mermaid source): #2513 (comment) to the README? |
I will add it, but I believe it describes the whole flow not just the service, also probably it will need to be updated with renewal endpoint flow but this will be part of renewal PR |
9a960da to
39784fe
Compare
… be based on interfaces
…ency with libre-graph-api
…pired and shareRemoved events, common config loading fix
39784fe to
c1dc12d
Compare
Guest Auth service:
Breaking change
Existing installations will not start until
AUTH_GUEST_JWT_SECRETis set(the new
auth-guestservice is part of the default service set and requiresit; there is intentionally no fallback to
OC_JWT_SECRET).Migration:
AUTH_GUEST_JWT_SECRET=<random secret>in the environment, oropencloud init).closes: #3069
dependent on: opencloud-eu/libre-graph-api#76