1
+ # OAuth daemon
2
+ # Copyright (C) 2014 Webshell SAS
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+
18
+ async = require ' async'
19
+ qs = require ' querystring'
20
+ Url = require ' url'
21
+ restify = require ' restify'
22
+ request = require ' request'
23
+ zlib = require ' zlib'
24
+ fs = require ' fs'
25
+ Stream = require ' stream'
26
+
27
+ module .exports = (env ) ->
28
+ oauth = env .engine .oauth
29
+
30
+ fixUrl = (ref ) -> ref .replace / ^ ([a-zA-Z\-_ ] + :\/ )([^ \/ ] )/ , ' $1/$2'
31
+
32
+
33
+ sendAbsentFeatureError = (req , res , feature ) ->
34
+ res .send 501 , " This provider does not support the " + feature + " feature yet"
35
+
36
+ cors_middleware = (req , res , next ) ->
37
+ oauthio = req .headers .oauthio
38
+ if ! oauthio
39
+ return cb new @check .Error " You must provide a valid 'oauthio' http header"
40
+ oauthio = qs .parse (oauthio)
41
+ if ! oauthio .k
42
+ return cb new @check .Error " oauthio_key" , " You must provide a 'k' (key) in 'oauthio' header"
43
+
44
+ origin = null
45
+ ref = fixUrl (req .headers [' referer' ] || req .headers [' origin' ] || " http://localhost" );
46
+ urlinfos = Url .parse (ref)
47
+ if not urlinfos .hostname
48
+ ref = origin = " http://localhost"
49
+ else
50
+ origin = urlinfos .protocol + ' //' + urlinfos .host
51
+ res .setHeader ' Access-Control-Allow-Origin' , origin
52
+ res .setHeader ' Access-Control-Allow-Methods' , ' GET, POST, PUT, PATCH, DELETE'
53
+ next ()
54
+
55
+ fieldMap = (body , map_array , filter ) ->
56
+ result = {}
57
+ for k of map_array
58
+ field = map_array[k]
59
+ if ! filter || k in filter
60
+ if typeof field == ' string'
61
+ if field == ' ='
62
+ result[k] = body[k]
63
+ else
64
+ result[k] = body[field]
65
+ else if typeof field == ' function'
66
+ result[k] = field (body)
67
+ result .raw = if result .raw then result .raw else body
68
+ return result
69
+
70
+ exp = {}
71
+ exp .raw = ->
72
+ fixUrl = (ref ) -> ref .replace / ^ ([a-zA-Z\-_ ] + :\/ )([^ \/ ] )/ , ' $1/$2'
73
+
74
+ check = @check
75
+ @server .opts new RegExp (' ^/auth/([a-zA-Z0-9_\\ .~-]+)/me$' ), (req , res , next ) =>
76
+
77
+ origin = null
78
+ ref = fixUrl (req .headers [' referer' ] || req .headers [' origin' ] || " http://localhost" );
79
+ urlinfos = Url .parse (ref)
80
+ if not urlinfos .hostname
81
+ return next new restify.InvalidHeaderError ' Missing origin or referer.'
82
+ origin = urlinfos .protocol + ' //' + urlinfos .host
83
+
84
+ res .setHeader ' Access-Control-Allow-Origin' , origin
85
+ res .setHeader ' Access-Control-Allow-Methods' , ' GET, POST, PUT, PATCH, DELETE'
86
+ if req .headers [' access-control-request-headers' ]
87
+ res .setHeader ' Access-Control-Allow-Headers' , req .headers [' access-control-request-headers' ]
88
+ res .cache maxAge : 120
89
+
90
+ res .send 200
91
+ next false
92
+
93
+ @server .get new RegExp (' ^/auth/([a-zA-Z0-9_\\ .~-]+)/me$' ), restify .queryParser (), cors_middleware, (req , res , next ) =>
94
+ cb = @server .send res, next
95
+ provider = req .params [0 ]
96
+ filter = req .query .filter
97
+ filter = filter ? .split ' ,'
98
+ oauthio = req .headers .oauthio
99
+ if ! oauthio
100
+ return cb new Error " You must provide a valid 'oauthio' http header"
101
+ oauthio = qs .parse (oauthio)
102
+ if ! oauthio .k
103
+ return cb new Error " oauthio_key" , " You must provide a 'k' (key) in 'oauthio' header"
104
+ @db .providers .getMeMapping provider, (err , content ) =>
105
+ if ! err
106
+ if content .url
107
+ @ apiRequest {apiUrl : content .url , headers : { ' User-Agent' : ' Node' } }, provider, oauthio, (err , options ) =>
108
+ return sendAbsentFeatureError (req, res, ' me()' ) if err
109
+ options .json = true
110
+ request options, (err , response , body ) =>
111
+ return sendAbsentFeatureError (req, res, ' me()' ) if err
112
+ # parsing body and mapping values to common field names, and sending the result
113
+ res .send fieldMap (body, content .fields , filter)
114
+ else if content .fetch
115
+ user_fetcher = {}
116
+ apiRequest = @apiRequest
117
+ async .eachSeries content .fetch , (item , cb ) ->
118
+ if typeof item == ' object'
119
+ url = item .url
120
+ apiRequest {apiUrl : content .url , headers : { ' User-Agent' : ' Node' } }, provider, oauthio, (err , options ) =>
121
+ return sendAbsentFeatureError (req, res, ' me()' ) if err
122
+ options .json = true
123
+ rq = request options, (err , response , body ) =>
124
+ for k of item .export
125
+ value = item .export [k](body)
126
+ user_fetcher[k] = value
127
+ cb ()
128
+ chunks = []
129
+ rq .on ' response' , (rs ) ->
130
+ rs .on ' data' , (chunk ) ->
131
+ chunks .push chunk
132
+ rs .on ' end' , ->
133
+ buffer = Buffer .concat chunks
134
+ if rs .headers [' content-encoding' ] == ' gzip'
135
+ zlib .gunzip buffer, (err , decoded ) ->
136
+ res .send 500 , err if err
137
+ body = JSON .parse decoded .toString ()
138
+ for k of item .export
139
+ value = item .export [k](body)
140
+ user_fetcher[k] = value
141
+ cb ()
142
+ else
143
+ body = JSON .parse buffer .toString ()
144
+ for k of item .export
145
+ value = item .export [k](body)
146
+ user_fetcher[k] = value
147
+ cb ()
148
+ if typeof item == ' function'
149
+ url = item (user_fetcher)
150
+ apiRequest {apiUrl : url, headers : { ' User-Agent' : ' Node' } }, provider, oauthio, (err , options ) =>
151
+ return sendAbsentFeatureError (req, res, ' me()' ) if err
152
+ options .json = true
153
+
154
+ options .headers [' accept-encoding' ] = undefined
155
+ rq = request options
156
+ chunks = []
157
+ rq .on ' response' , (rs ) ->
158
+ rs .on ' data' , (chunk ) ->
159
+ chunks .push chunk
160
+ rs .on ' end' , ->
161
+ buffer = Buffer .concat chunks
162
+ if rs .headers [' content-encoding' ] == ' gzip'
163
+ zlib .gunzip buffer, (err , decoded ) ->
164
+ res .send 500 , err if err
165
+ body = JSON .parse decoded .toString ()
166
+ res .send fieldMap (body, content .fields , filter)
167
+ else
168
+ body = JSON .parse buffer .toString ()
169
+ res .send fieldMap (body, content .fields , filter)
170
+
171
+
172
+ , ->
173
+ else
174
+ return sendAbsentFeatureError (req, res, ' me()' )
175
+ else
176
+ return sendAbsentFeatureError (req, res, ' me()' )
177
+ exp
0 commit comments