-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrealm.py
151 lines (119 loc) · 4.59 KB
/
realm.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
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""Implement a base class for L{twisted.cred} Realms.
This contains an "avatar pool" which is required as the spec stipulates
clients can re-establish sessions after a severed connection
"""
from __future__ import absolute_import, division
import time
from base import SMBError
from zope.interface import implementer, Interface, Attribute
from twisted.logger import Logger
from twisted.cred.portal import IRealm
from twisted.internet.defer import maybeDeferred
from twisted.internet.task import LoopingCall
log = Logger()
MAX_COUNTER=2**64 - 1
POOL_CLEAN_INTERVAL=600
POOL_AVATAR_EXPIRY=1200
@implementer(IRealm)
class SMBBaseRealm:
"""base class for all realms for use with SMB server
Holds avatar pool so can be retrieved by prev_session_id
"""
def __init__(self):
self.pool = {}
self.counter = 1
self.loop = LoopingCall(self.clean_pool)
self.loop.start(POOL_CLEAN_INTERVAL, False)
def chainedRequestAvatar(self, avatarId, mind, *interfaces):
"""descendants override this
same signature as L{twisted.cred.portal.IRealm.requestAvatar}
"""
pass
def requestAvatar(self, avatarId, mind, *interfaces):
if mind.session_id == 0:
self.counter += 1
if self.counter >= MAX_COUNTER:
self.counter = 1
while self.counter in self.pool:
self.counter += 1
if self.counter >= MAX_COUNTER:
self.counter = 1
d = maybeDeferred(self.chainedRequestAvatar, avatarId, mind, *interfaces)
d.addCallback(self._cb_avatar, self.counter)
return d
else:
try:
node = self.pool[mind.session_id]
except KeyError:
raise SMBError("session no longer available")
node.activate()
return (ISMBServer, node.avatar, lambda: self.logout(mind.session_id))
def _cb_avatar(self, t, session_id):
interface, avatar, inner_logout = t
avatar.session_id = session_id
self.pool[session_id] = Node(avatar, inner_logout)
return (interface, avatar, lambda: self.logout(session_id))
def logout(self, session_id):
"""notify the pool and an avatar of a formal (planned) logout
"""
self.pool[session_id].inner_logout()
del self.pool[session_id]
def connectionLost(self, session_id):
"""notify pool a connection has been lost without logout
"""
try:
self.pool[session_id].deactivate()
except KeyError:
pass
def clean_pool(self):
"""remove avatars absndoned for so long reconnection is unlikely
"""
now = time.time()
for i in self.pool.keys():
if self.pool[i].expired(now):
self.pool[i].inner_logout()
del self.pool[i]
def shutdown(self):
"""shutdown loop timer """
self.loop.stop()
class Node:
"""member of the pool, holds avatar and timer for expiry
"""
def __init__(self, avatar, inner_logout):
self.avatar = avatar
self.inner_logout = inner_logout
self.inactive_time = None
def activate(self):
"""mark node as acfive, i.e. stop the clock
"""
self.inactive_time = None
def deactivate(self):
"""mark node as inactive, i.e. start the clock
"""
self.inactive_time = time.time()
def expired(self, now):
"""test if deactivated too long
@param now: current time, to avoid repeatingly calling L{time.time()}
@type now: float
@rtype: bool
"""
return self.inactive_time and now - self.inactive_time > POOL_AVATAR_EXPIRY
class TestRealm(SMBBaseRealm):
def chainedRequestAvatar(self, avatarId, mind, interfaces):
log.debug("avatarId=%r mind=%r" % (avatarId, mind))
return (ISMBServer, TestAvatar(), lambda: None)
class ISMBServer(Interface):
"""
A SMB server avatar, contains a number of "shares" (filesystems/printers/
IPCs)
"""
session_id = Attribute("the assigned int64 session ID")
@implementer(ISMBServer)
class TestAvatar():
"""
a test avatar that illustrates functionality applications need to
implement
"""
pass