public
Description: An HTTP proxy for signing OAuth requests
Clone URL: git://github.com/mojodna/oauth-proxy.git
Search Repo:
doc cleanup
mojodna (author)
Sun May 11 08:55:12 -0700 2008
commit  2eaf3aea5cb9f5b802892601a5b8630364943007
tree    ff7096124735a32f32d7795491904ddea9583aeb
parent  4c8a589db43ae8d3b3a88a90931e09aac4183480
0
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
@@ -1,16 +1 @@
0
-This is an OAuth proxy. It allows vanilla requests to be funneled through it and signed
0
-appropriately using OAuth. For now, it only uses a single, pre-set access token.
0
-
0
-A skeleton also exists for a reverse proxy. No OAuth signature validation is done yet, so go for it!
0
-
0
-To create an OAuth proxy server:
0
-mktap oauth_proxy --consumer-key <consumer key> --consumer-secret <consumer secret> [--token <token>] [--token-secret <token secret>] [-p <proxy port>] [--ssl]
0
-
0
-Start the generated tap (in the foreground):
0
-twistd -nf oauth_proxy.tap
0
-
0
-(For mktap to find oauth_proxy, you may need to make sure that "." is in your PYTHONPATH)
0
-
0
-Or, just run it with twistd:
0
-twistd -n oauth_proxy --consumer-key <consumer key> --consumer-secret <consumer secret> [--token <token>] [--token-secret <token secret>] [-p <proxy port>] [--ssl]
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
0
@@ -1 +1,24 @@
0
+# OAuth Reverse Proxy
0
+
0
+I am an OAuth proxy server. You can pass unsigned requests to me and I will sign them using [OAuth](http://oauth.net/ "OAuth") before sending them to their eventual destination.
0
+
0
+At the moment, tokens and consumer keys are configurable only at start-time, so individual proxies are limited to a single pair at a time.
0
+
0
+
0
+## Running
0
+
0
+Provided that "." is in your `PYTHONPATH`, you should be able to run the proxy with `twistd`:
0
+
0
+ twistd -n oauth_proxy --consumer-key <consumer key> --consumer-secret <consumer secret> [--token <token>] [--token-secret <token secret>] [-p <proxy port>] [--ssl]
0
+
0
+
0
+## Running as a daemon
0
+
0
+You may run the proxy with `twistd` directly (omitting the _-n_ argument) or you may generate a pre-configured tap, which can then be packaged and distributed. To generate a tap:
0
+
0
+ mktap oauth_proxy --consumer-key <consumer key> --consumer-secret <consumer secret> [--token <token>] [--token-secret <token secret>] [-p <proxy port>] [--ssl]
0
+
0
+To run the tap (using the settings that were provided when creating it):
0
+
0
+ twistd -f oauth_proxy.tap
0
...
1
...
 
0
@@ -1,2 +1 @@
0
-- create oauth_proxy.tac (similar to oauth_proxy/tap.py) for use with twistd, without requiring mktap (see http://twistedmatrix.com/projects/core/documentation/howto/application.html)
...
22
23
24
25
 
26
27
28
29
30
31
32
 
33
34
35
 
36
37
38
39
...
43
44
45
46
 
47
48
49
50
51
 
52
53
54
55
56
...
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
...
112
113
114
115
 
 
 
116
117
118
...
123
124
125
126
 
127
128
129
...
22
23
24
 
25
26
27
28
29
30
31
 
32
33
34
 
35
36
37
38
39
...
43
44
45
 
46
47
48
49
50
 
51
52
53
54
55
56
...
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
...
112
113
114
 
115
116
117
118
119
120
...
125
126
127
 
128
129
130
131
0
@@ -22,17 +22,17 @@
0
 
0
 class IOAuthCredentialProvider(Interface):
0
   """An OAuth credential provider"""
0
-
0
+
0
   def fetchCredentials():
0
     """Fetch credentials"""
0
 
0
 
0
 class StaticOAuthCredentialProvider:
0
   implements(IOAuthCredentialProvider)
0
-
0
+
0
   def __init__(self, credentials):
0
     self.credentials = credentials
0
-
0
+
0
   def fetchCredentials(self):
0
     return self.credentials
0
 
0
0
@@ -43,12 +43,12 @@
0
   """
0
   def __init__(self, consumerKey, consumerSecret, token = None, tokenSecret = None, signatureMethod = oauth.OAuthSignatureMethod_HMAC_SHA1()):
0
     self.oauthConsumer = oauth.OAuthConsumer(consumerKey, consumerSecret)
0
-
0
+
0
     if token is not None and tokenSecret is not None:
0
       self.oauthToken = oauth.OAuthToken(token, tokenSecret)
0
     else:
0
       self.oauthToken = None
0
-
0
+
0
     self.signatureMethod = signatureMethod
0
 
0
 
0
0
0
@@ -78,18 +78,18 @@
0
   def buildProtocol(self, addr):
0
     credentials = self.father.credentialProvider.fetchCredentials()
0
     oauthRequest = self.signRequest(credentials)
0
-
0
+
0
     client = proxy.ProxyClientFactory.buildProtocol(self, addr)
0
     # upgrade proxy.proxyClient object to OAuthProxyClient
0
     client.__class__ = OAuthProxyClient
0
     client.factory = self
0
-
0
+
0
     client.headers.update(oauthRequest.to_header())
0
     return client
0
 
0
   def signRequest(self, credentials):
0
     """Create an OAuthRequest and sign it"""
0
-
0
+
0
     if self.father.useSSL:
0
       path = self.father.path.replace("http", "https", 1)
0
     else:
0
0
@@ -97,9 +97,9 @@
0
 
0
     # python parses arguments into a dict of arrays, e.g. 'q=foo' becomes {'q': ['foo']}
0
     # while from_consumer_and_token expects a dict of strings, so we cross our fingers,
0
- # hope there are no repeated arguments ('q=foo&q=bar'), and take the first value of
0
+ # hope there are no repeated arguments ('q=foo&q=bar'), and take the last value of
0
     # each array.
0
- args = dict([(k,v[0]) for k,v in self.father.args.items()])
0
+ args = dict([(k,v[-1]) for k,v in self.father.args.items()])
0
 
0
     # create an OAuth Request from the pieces that we've assembled
0
     oauthRequest = oauth.OAuthRequest.from_consumer_and_token(
0
@@ -112,7 +112,9 @@
0
 
0
     # now, sign it
0
     oauthRequest.sign_request(credentials.signatureMethod, credentials.oauthConsumer, credentials.oauthToken)
0
-
0
+
0
+ # TODO add X-Forwarded-For headers
0
+
0
     return oauthRequest
0
 
0
 
0
@@ -123,7 +125,7 @@
0
     self.credentialProvider = credentialProvider
0
     self.useSSL = useSSL
0
     proxy.ProxyRequest.__init__(self, *args)
0
-
0
+
0
     if self.useSSL:
0
       # Since we magically mapped HTTP to HTTPS, we want to make sure that the transport knows as much
0
       self._forceSSL = True
...
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
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
@@ -1,112 +1 @@
0
-from twisted.internet import reactor
0
-from twisted.python import usage
0
-from twisted.web import proxy
0
-import urlparse
0
-
0
-class Options(usage.Options):
0
- synopsis = "Usage: oauth_reverse proxy --remote-host <remote host> [--remote-port <remote port>] [--path-prefix <path prefix>] [-p <proxy port>] [--ssl] [--ssl-private-key <private key] [--ssl-certificate <certificate>]"
0
- longdesc = "Makes an OAuth reverse HTTP proxy server.."
0
- optParameters = [
0
- ['path-prefix', None, '', "Path prefix"],
0
- ['port', 'p', 8080, "Proxy port", int],
0
- ['remote-host', None, None, "Remote host"],
0
- ['remote-port', None, 80, "Remote port"],
0
- ['ssl-certificate', None, None, "SSL certificate"],
0
- ['ssl-private-key', None, None, "SSL private key"],
0
- ]
0
-
0
- optFlags = [['ssl', 's']]
0
-
0
-
0
-
0
-class OAuthValidator:
0
- def validate(self):
0
- """Validate an OAuth request"""
0
- return True
0
-
0
-
0
-
0
-class OAuthReverseProxyRequest(proxy.ReverseProxyRequest):
0
- # TODO this class may be unnecessary if header rewriting can occur in OAuthReverseProxyResource
0
-
0
- proxyClientFactoryClass = ProxyClientFactory
0
-
0
- def __init__(self, validator, *args):
0
- self.validator = validator
0
- proxy.ReverseProxyRequest.__init__(self, *args)
0
-
0
-
0
- def process(self):
0
- # This logic either goes here or in OAuthReverseProxyResource.render
0
-
0
- # filter querystring from self.uri
0
-
0
- # filter headers from self.getAllHeaders()
0
-
0
- # validate oauth params
0
- valid = True
0
-
0
- if valid:
0
- proxy.ReverseProxyRequest.process(self)
0
- # looks like:
0
- # self.received_headers['host'] = self.factory.host
0
- # clientFactory = self.proxyClientFactoryClass(
0
- # self.method, self.uri, self.clientproto, self.getAllHeaders(),
0
- # self.content.read(), self)
0
- # self.reactor.connectTCP(self.factory.host, self.factory.port,
0
- # clientFactory)
0
- else:
0
- # return an error message
0
- pass
0
-
0
-
0
-
0
-class OAuthReverseProxy(proxy.ReverseProxy):
0
-
0
- # TODO this may be the only required line, if validation occurs in OAuthReverseProxyResource
0
- # requestFactory = OAuthReverseProxyRequest
0
-
0
- def __init__(self, validator):
0
- self.validator = validator
0
- proxy.ReverseProxy.__init__(self)
0
-
0
-
0
- def requestFactory(self, *args):
0
- return OAuthReverseProxyRequest(self.validator, *args)
0
-
0
-
0
-
0
-class OAuthReverseProxyResource(proxy.ReverseProxyResource):
0
- def __init__(self, host, port, path, reactor=reactor, validator=OAuthValidator):
0
- self.validator = validator
0
- proxy.ReverseProxyResource(self, host, port, path, reactor)
0
-
0
-
0
- def getChild(self, path, request):
0
- return OAuthReverseProxyResource(
0
- self.host, self.port, self.path + '/' + urlquote(path, safe="", validator=self.validator))
0
-
0
-
0
- def render(self, request):
0
- # get OAuth headers from request.received_headers['authorization']
0
-
0
- # remove OAuth headers
0
-
0
- # parse querystring and POST body for OAuth params
0
- qs = urlparse.urlparse(request.uri)[4]
0
-
0
- # rewrite the path w/o OAuth params
0
-
0
- # validate signature
0
-
0
- valid = self.validator.validate()
0
-
0
- if valid:
0
- response = proxy.ReverseProxyResource.render(self, request)
0
- else:
0
- # render an error message
0
- # TODO return NOT_DONE_YET in order to write headers
0
- response = "Invalid signature"
0
-
0
- return response
...
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
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
@@ -1,39 +1 @@
0
-from zope.interface import implements
0
-
0
-from twisted.plugin import IPlugin
0
-from twisted.application.service import IServiceMaker
0
-from twisted.application import internet
0
-
0
-# don't include SSL if it's not installed
0
-try:
0
- from twisted.internet import ssl
0
-except ImportError:
0
- pass
0
-
0
-from twisted.web import proxy, server
0
-
0
-from oauth_proxy import oauth_proxy, reverse_proxy
0
-
0
-class OAuthReverseProxyServiceMaker(object):
0
- implements(IServiceMaker, IPlugin)
0
- tapname = "oauth_reverse_proxy"
0
- description = "OAuth reverse HTTP proxy"
0
- options = reverse_proxy.Options
0
-
0
- def makeService(self, options):
0
- # TODO add error handling for missing params
0
-
0
- remoteHost = options["remote-host"]
0
- remotePort = options["remote-port"]
0
- pathPrefix = options["path-prefix"]
0
- proxyPort = options["port"]
0
-
0
- site = server.Site(reverse_proxy.OAuthReverseProxyResource(remoteHost, remotePort, pathPrefix))
0
-
0
- if options["ssl"]:
0
- server = internet.SSLServer(proxyPort, site, ssl.DefaultOpenSSLContextFactory(options["ssl-private-key"], options["ssl-certificate"]))
0
- else:
0
- server = internet.TCPServer(proxyPort, site)
0
-
0
-serviceMaker = OAuthReverseProxyServiceMaker()

Comments

    No one has commented yet.