Chore Account server

Check-in [26b310cf55]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Bringup, account server connection
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | master | trunk
Files: files | file ages | folders
SHA3-256:26b310cf557a015ef39ed6f2833cb27557c4b65ade0236f3bccc6c27ad7d01ac
User & Date: ajv-899-334-8894@vsta.org 2016-10-31 21:28:16
Context
2016-11-13
20:09
Bringup, account server check-in: 82fbdb4e98 user: ajv-899-334-8894@vsta.org tags: master, trunk
2016-10-31
21:28
Bringup, account server connection check-in: 26b310cf55 user: ajv-899-334-8894@vsta.org tags: master, trunk
2016-10-26
23:15
Edge case check-in: 9177c5536e user: ajv-899-334-8894@vsta.org tags: master, trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to get.py.

64
65
66
67
68
69
70


71

72
73
74
75
76
77
78
            buf += "<ul>\n"
            for nm,cfg in services.iteritems():
                # Get service configuration; we need their port number
                #  to build the URL our client can use to reach it.
                if nm not in app.services:
                    # Don't list services which are not started
                    continue


                sport = app.services[nm][1]


                # Service URL
                surl = "http%s://%s:%d" % \
                 (("s" if server.ssl else ""), host, sport)
                buf += ' <li><a href="%s">%s</a></li>\n' % (surl, nm)
                nservice += 1








>
>
|
>







64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
            buf += "<ul>\n"
            for nm,cfg in services.iteritems():
                # Get service configuration; we need their port number
                #  to build the URL our client can use to reach it.
                if nm not in app.services:
                    # Don't list services which are not started
                    continue

                # Port for non-SSL, then SSL
                sport = app.services[nm][1] \
                    [0 if not server.ssl else 1]

                # Service URL
                surl = "http%s://%s:%d" % \
                 (("s" if server.ssl else ""), host, sport)
                buf += ' <li><a href="%s">%s</a></li>\n' % (surl, nm)
                nservice += 1

Changes to main.py.

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
...
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
...
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216






























217
218
219
220
221
222
223
#
# account.py
#	Manage user accounts across chore services
#

import sys, os, threading, socket
import chore
from get import GET_mixin
from post import POST_mixin




# Trap monkey business with UDS path names
def okname(s):


    if not s.startswith("/tmp/"):
        return False
    s = s[5:]
    tup = s.split('-')
    if len(tup) != 2:
        return False
    for s in tup:
        if not s:
            return False
        if any( (not c.isalnum()) for c in s ):
            return False
    return True

# Overlay an account config
def merge_account(src, dest):
    for k,v in src.iteritems():

................................................................................
# This server acts as a portal, tabulating configured services for
#  any particular user.  By coming through this portal, users can
#  also refresh expiring cookies via a new round of authentication
#  before landing on their desired service.
#
# cfg{} - Top-level configuration
# accts{},accts_tm - Config of accounts, and st_mtime when read
# nonces{} - Map from server ID to nonce value (to detect
#       corrupt or mis-directed traffic)
class AccountsServer(chore.server.Server):
    def __init__(self, cfg, domain="chore"):
        self.domain = domain

        # Static account config
        self.cfg = cfg

        # Per-server nonce values
        self.nonces = {}

        # Services, as they register to us.
        # Map from service name to (PID, port#, socket-name)
        self.services = {}

        # Load current version of accounts
        self.accts,self.accts_tm = load_accounts(cfg["accounts"])
................................................................................
        s.send(json.dumps(req))
        s.close()

    # Process this request.  The request is a dict extracted
    #  from JSON, with at least:
    # reply-to - socket for answer
    # op - string name of operation
    # nonce - Random value, invariant from this sender
    #  over the life of its service.
    def serve(self, req):
        op = req["op"]
        resp = req["reply-to"]

        # Start of service
        if op == "start":
            # Check nonce if needed
            if resp in self.nonces:
                if self.nonces[resp] != req["nonce"]:
                    sys.stderr.write("Bad nonce from '%s'\n" %
                        (resp,))
                    self.reply(resp, req["op"], "?Nonce")
                    return
            self.nonces[resp] = req["nonce"]

            # Register service
            self.services[req["service"]] = \
             (req["pid"], req["port"], resp)






























            self.reply("OK", req)
            return

        # Unknown
        sys.stderr.write("Unknown op '%s' from '%s'\n" %
            (op, resp))
        self.reply(resp, req["op"], "?Bad-op")




>
|



>
>
>



>
>









|







 







|








|
|







 







|







|
|
|
|



|




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
...
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
...
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
#
# account.py
#	Manage user accounts across chore services
#
import pdb
import sys, os, threading, socket, json
import chore
from get import GET_mixin
from post import POST_mixin

# Extra chars for service names, beyond isalnum()
OkChars = set([' ', '_'])

# Trap monkey business with UDS path names
def okname(s):
    global OkChars

    if not s.startswith("/tmp/"):
        return False
    s = s[5:]
    tup = s.split('-')
    if len(tup) != 2:
        return False
    for s in tup:
        if not s:
            return False
        if any( ((not c.isalnum()) and (c not in OkChars) for c in s) ):
            return False
    return True

# Overlay an account config
def merge_account(src, dest):
    for k,v in src.iteritems():

................................................................................
# This server acts as a portal, tabulating configured services for
#  any particular user.  By coming through this portal, users can
#  also refresh expiring cookies via a new round of authentication
#  before landing on their desired service.
#
# cfg{} - Top-level configuration
# accts{},accts_tm - Config of accounts, and st_mtime when read
# magics{} - Map from server ID to magic value (to detect
#       corrupt or mis-directed traffic)
class AccountsServer(chore.server.Server):
    def __init__(self, cfg, domain="chore"):
        self.domain = domain

        # Static account config
        self.cfg = cfg

        # Per-server magic values
        self.magics = {}

        # Services, as they register to us.
        # Map from service name to (PID, port#, socket-name)
        self.services = {}

        # Load current version of accounts
        self.accts,self.accts_tm = load_accounts(cfg["accounts"])
................................................................................
        s.send(json.dumps(req))
        s.close()

    # Process this request.  The request is a dict extracted
    #  from JSON, with at least:
    # reply-to - socket for answer
    # op - string name of operation
    # magic - Random value, invariant from this sender
    #  over the life of its service.
    def serve(self, req):
        op = req["op"]
        resp = req["reply-to"]

        # Start of service
        if op == "start":
            # Check magic if needed
            if resp in self.magics:
                if self.magics[resp] != req["magic"]:
                    sys.stderr.write("Bad magic from '%s'\n" %
                        (resp,))
                    self.reply(resp, req["op"], "?Nonce")
                    return
            self.magics[resp] = req["magic"]

            # Register service
            self.services[req["service"]] = \
             (req["pid"], req["port"], resp)

            # Replace their port specification with our own
            req["port"] = self.http_ports()

            # And send back registration success
            self.reply("OK", req)
            return

        # Sanity check--once started, we should have their magic
        #  value.
        if (resp not in self.magics) or \
                (self.magics[resp] != req["magic"]):
            sys.stderr.write("Bad magic from '%s'\n" % (resp,))
            return

        # Check cookie
        if op == "cookie?":
            # Correct format?
            tup = self.parse_cookie(req.get("cookie", "XXX"))
            if tup is None:
                self.reply("?format", req)
                return

            # Ok cookie value?
            user,cookie = tup
            if not self.valid_cookie(user, cookie):
                self.reply("?authen", req)
                return

            # Let them on
            self.reply("OK", req)
            return

        # Unknown
        sys.stderr.write("Unknown op '%s' from '%s'\n" %
            (op, resp))
        self.reply(resp, req["op"], "?Bad-op")