/
sharing.go
129 lines (117 loc) · 3.26 KB
/
sharing.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
127
128
129
package move
import (
"bytes"
"encoding/json"
"errors"
"net/http"
"net/url"
"strings"
"time"
"github.com/cozy/cozy-stack/client/request"
"github.com/cozy/cozy-stack/model/instance"
"github.com/cozy/cozy-stack/model/oauth"
"github.com/cozy/cozy-stack/model/permission"
"github.com/cozy/cozy-stack/model/sharing"
"github.com/cozy/cozy-stack/pkg/consts"
"github.com/cozy/cozy-stack/pkg/couchdb"
"github.com/cozy/cozy-stack/pkg/jsonapi"
multierror "github.com/hashicorp/go-multierror"
"github.com/labstack/echo/v4"
)
// NotifySharings will notify other instances with a common sharing that this
// instance has moved, and will tell them to the new URL to use for the
// sharing.
func NotifySharings(inst *instance.Instance) error {
// Let the dust settle a bit before starting the notifications
time.Sleep(3 * time.Second)
var sharings []*sharing.Sharing
req := couchdb.AllDocsRequest{Limit: 1000}
if err := couchdb.GetAllDocs(inst, consts.Sharings, &req, &sharings); err != nil {
return err
}
var errm error
for _, s := range sharings {
if strings.HasPrefix(s.ID(), "_design") {
continue
}
time.Sleep(100 * time.Millisecond)
if err := notifySharing(inst, s); err != nil {
errm = multierror.Append(errm, err)
}
}
return errm
}
func notifySharing(inst *instance.Instance, s *sharing.Sharing) error {
if !s.Owner {
return notifyMember(inst, s, 0)
}
var errm error
for i := range s.Members {
if i == 0 {
continue // skip the owner
}
if err := notifyMember(inst, s, i); err != nil {
errm = multierror.Append(errm, err)
}
}
return errm
}
func notifyMember(inst *instance.Instance, s *sharing.Sharing, index int) error {
u, err := url.Parse(s.Members[index].Instance)
if s.Members[index].Instance == "" || err != nil {
return err
}
clientID := s.Credentials[0].InboundClientID
if index > 0 {
clientID = s.Credentials[index-1].InboundClientID
}
cli := &oauth.Client{ClientID: clientID}
newToken, err := sharing.CreateAccessToken(inst, cli, s.ID(), permission.ALL)
if err != nil {
return err
}
moved := sharing.APIMoved{
SharingID: s.ID(),
NewInstance: inst.PageURL("", nil),
AccessToken: newToken.AccessToken,
RefreshToken: newToken.RefreshToken,
}
data, err := jsonapi.MarshalObject(&moved)
if err != nil {
return err
}
body, err := json.Marshal(jsonapi.Document{Data: &data})
if err != nil {
return err
}
credIndex := 0
if s.Owner {
credIndex = index - 1
}
if len(s.Credentials) <= credIndex || s.Credentials[credIndex].AccessToken == nil {
return errors.New("sharing in invalid state")
}
token := s.Credentials[credIndex].AccessToken.AccessToken
opts := &request.Options{
Method: http.MethodPost,
Scheme: u.Scheme,
Domain: u.Host,
Path: "/sharings/" + s.SID + "/recipients/self/moved",
Headers: request.Headers{
echo.HeaderAccept: jsonapi.ContentType,
echo.HeaderContentType: jsonapi.ContentType,
echo.HeaderAuthorization: "Bearer " + token,
},
Body: bytes.NewReader(body),
ParseError: sharing.ParseRequestError,
}
res, err := request.Req(opts)
if res != nil && res.StatusCode/100 == 4 {
res, err = sharing.RefreshToken(inst, err, s, &s.Members[index], &s.Credentials[credIndex], opts, body)
}
if err != nil {
return err
}
defer res.Body.Close()
return nil
}