Skip to content

Commit 9b6adec

Browse files
committed
feat: sharepoint webdav (unfinished)
1 parent b3540cf commit 9b6adec

File tree

4 files changed

+209
-2
lines changed

4 files changed

+209
-2
lines changed

drivers/189/189.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ func (driver Cloud189) Login(account *model.Account) error {
132132
}
133133
}
134134
if lt == "" {
135-
return fmt.Errorf("get empty login page")
135+
return errors.New(b)
136136
}
137137
captchaToken := regexp.MustCompile(`captchaToken' value='(.+?)'`).FindStringSubmatch(b)[1]
138138
returnUrl := regexp.MustCompile(`returnUrl = '(.+?)'`).FindStringSubmatch(b)[1]

drivers/webdav/driver.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@ func (driver WebDav) Items() []base.Item {
4040
Type: base.TypeString,
4141
Required: true,
4242
},
43+
{
44+
Name: "internal_type",
45+
Label: "vendor",
46+
Type: base.TypeSelect,
47+
Required: true,
48+
Default: "other",
49+
Values: "sharepoint,other",
50+
Description: "sharepoint temporarily unavailable",
51+
},
4352
}
4453
}
4554

drivers/webdav/odrvcookie/fetch.go

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
// Package odrvcookie can fetch authentication cookies for a sharepoint webdav endpoint
2+
package odrvcookie
3+
4+
import (
5+
"bytes"
6+
"encoding/xml"
7+
"html/template"
8+
"log"
9+
"net/http"
10+
"net/http/cookiejar"
11+
"net/url"
12+
"strings"
13+
"time"
14+
15+
"golang.org/x/net/publicsuffix"
16+
)
17+
18+
// CookieAuth hold the authentication information
19+
// These are username and password as well as the authentication endpoint
20+
type CookieAuth struct {
21+
user string
22+
pass string
23+
endpoint string
24+
}
25+
26+
// CookieResponse contains the requested cookies
27+
type CookieResponse struct {
28+
RtFa http.Cookie
29+
FedAuth http.Cookie
30+
}
31+
32+
// SuccessResponse hold a response from the sharepoint webdav
33+
type SuccessResponse struct {
34+
XMLName xml.Name `xml:"Envelope"`
35+
Succ SuccessResponseBody `xml:"Body"`
36+
}
37+
38+
// SuccessResponseBody is the body of a success response, it holds the token
39+
type SuccessResponseBody struct {
40+
XMLName xml.Name
41+
Type string `xml:"RequestSecurityTokenResponse>TokenType"`
42+
Created time.Time `xml:"RequestSecurityTokenResponse>Lifetime>Created"`
43+
Expires time.Time `xml:"RequestSecurityTokenResponse>Lifetime>Expires"`
44+
Token string `xml:"RequestSecurityTokenResponse>RequestedSecurityToken>BinarySecurityToken"`
45+
}
46+
47+
// reqString is a template that gets populated with the user data in order to retrieve a "BinarySecurityToken"
48+
const reqString = `<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
49+
xmlns:a="http://www.w3.org/2005/08/addressing"
50+
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
51+
<s:Header>
52+
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
53+
<a:ReplyTo>
54+
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
55+
</a:ReplyTo>
56+
<a:To s:mustUnderstand="1">https://login.microsoftonline.com/extSTS.srf</a:To>
57+
<o:Security s:mustUnderstand="1"
58+
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
59+
<o:UsernameToken>
60+
<o:Username>{{ .Username }}</o:Username>
61+
<o:Password>{{ .Password }}</o:Password>
62+
</o:UsernameToken>
63+
</o:Security>
64+
</s:Header>
65+
<s:Body>
66+
<t:RequestSecurityToken xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
67+
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
68+
<a:EndpointReference>
69+
<a:Address>{{ .Address }}</a:Address>
70+
</a:EndpointReference>
71+
</wsp:AppliesTo>
72+
<t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType>
73+
<t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
74+
<t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType>
75+
</t:RequestSecurityToken>
76+
</s:Body>
77+
</s:Envelope>`
78+
79+
// New creates a new CookieAuth struct
80+
func New(pUser, pPass, pEndpoint string) CookieAuth {
81+
retStruct := CookieAuth{
82+
user: pUser,
83+
pass: pPass,
84+
endpoint: pEndpoint,
85+
}
86+
87+
return retStruct
88+
}
89+
90+
// Cookies creates a CookieResponse. It fetches the auth token and then
91+
// retrieves the Cookies
92+
func (ca *CookieAuth) Cookies() (CookieResponse, error) {
93+
return ca.getSPCookie(ca.getSPToken())
94+
}
95+
96+
func (ca *CookieAuth) getSPCookie(conf *SuccessResponse) (CookieResponse, error) {
97+
spRoot, err := url.Parse(ca.endpoint)
98+
if err != nil {
99+
panic(err)
100+
}
101+
102+
u, err := url.Parse("https://" + spRoot.Host + "/_forms/default.aspx?wa=wsignin1.0")
103+
if err != nil {
104+
log.Fatal(err)
105+
}
106+
107+
// To authenticate with davfs or anything else we need two cookies (rtFa and FedAuth)
108+
// In order to get them we use the token we got earlier and a cookieJar
109+
jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
110+
if err != nil {
111+
log.Fatal(err)
112+
}
113+
114+
client := &http.Client{
115+
Jar: jar,
116+
}
117+
118+
// Send the previously aquired Token as a Post parameter
119+
if _, err = client.Post(u.String(), "text/xml", strings.NewReader(conf.Succ.Token)); err != nil {
120+
log.Fatal(err)
121+
}
122+
123+
cookieResponse := CookieResponse{}
124+
for _, cookie := range jar.Cookies(u) {
125+
if (cookie.Name == "rtFa") || (cookie.Name == "FedAuth") {
126+
switch cookie.Name {
127+
case "rtFa":
128+
cookieResponse.RtFa = *cookie
129+
case "FedAuth":
130+
cookieResponse.FedAuth = *cookie
131+
}
132+
}
133+
}
134+
return cookieResponse, err
135+
}
136+
137+
func (ca *CookieAuth) getSPToken() *SuccessResponse {
138+
reqData := map[string]interface{}{
139+
"Username": ca.user,
140+
"Password": ca.pass,
141+
"Address": ca.endpoint,
142+
}
143+
144+
t := template.Must(template.New("authXML").Parse(reqString))
145+
146+
buf := &bytes.Buffer{}
147+
if err := t.Execute(buf, reqData); err != nil {
148+
panic(err)
149+
}
150+
151+
// Execute the first request which gives us an auth token for the sharepoint service
152+
// With this token we can authenticate on the login page and save the returned cookies
153+
req, err := http.NewRequest("POST", "https://login.microsoftonline.com/extSTS.srf", buf)
154+
if err != nil {
155+
panic(err)
156+
}
157+
158+
client := &http.Client{}
159+
resp, err := client.Do(req)
160+
if err != nil {
161+
panic(err.Error())
162+
}
163+
defer resp.Body.Close()
164+
165+
respBuf := bytes.Buffer{}
166+
respBuf.ReadFrom(resp.Body)
167+
s := respBuf.Bytes()
168+
169+
var conf SuccessResponse
170+
err = xml.Unmarshal(s, &conf)
171+
if err != nil {
172+
panic(err)
173+
}
174+
175+
return &conf
176+
}

drivers/webdav/webdav.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,36 @@ package webdav
22

33
import (
44
"github.com/Xhofe/alist/drivers/base"
5+
"github.com/Xhofe/alist/drivers/webdav/odrvcookie"
56
"github.com/Xhofe/alist/model"
67
"github.com/Xhofe/alist/utils"
8+
"github.com/Xhofe/alist/utils/cookie"
9+
log "github.com/sirupsen/logrus"
710
"github.com/studio-b12/gowebdav"
11+
"net/http"
812
"strings"
913
)
1014

1115
func (driver WebDav) NewClient(account *model.Account) *gowebdav.Client {
12-
return gowebdav.NewClient(account.SiteUrl, account.Username, account.Password)
16+
c := gowebdav.NewClient(account.SiteUrl, account.Username, account.Password)
17+
if account.InternalType == "sharepoint" {
18+
19+
ca := odrvcookie.New(account.Username, account.Password, account.SiteUrl)
20+
tokenConf, err := ca.Cookies()
21+
log.Debugln(err, tokenConf)
22+
if err != nil {
23+
c.SetInterceptor(func(method string, rq *http.Request) {
24+
rq.Header.Del("Authorization")
25+
//rq.AddCookie(&tokenConf.FedAuth)
26+
//rq.AddCookie(&tokenConf.RtFa)
27+
rq.Header.Set("Cookie", cookie.ToString([]*http.Cookie{&tokenConf.RtFa, &tokenConf.FedAuth}))
28+
if method == "PROPFIND" {
29+
rq.Header.Set("Depth", "0")
30+
}
31+
})
32+
}
33+
}
34+
return c
1335
}
1436

1537
func (driver WebDav) WebDavPath(path string) string {

0 commit comments

Comments
 (0)