1: <?php
2:
3: /**
4: * This file supplies a memcached store backend for OpenID servers and
5: * consumers.
6: *
7: * PHP versions 4 and 5
8: *
9: * LICENSE: See the COPYING file included in this distribution.
10: *
11: * @package OpenID
12: * @author Artemy Tregubenko <me@arty.name>
13: * @copyright 2008 JanRain, Inc.
14: * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
15: * Contributed by Open Web Technologies <http://openwebtech.ru/>
16: */
17:
18: /**
19: * Import the interface for creating a new store class.
20: */
21: require_once 'Auth/OpenID/Interface.php';
22:
23: /**
24: * This is a memcached-based store for OpenID associations and
25: * nonces.
26: *
27: * As memcache has limit of 250 chars for key length,
28: * server_url, handle and salt are hashed with sha1().
29: *
30: * Most of the methods of this class are implementation details.
31: * People wishing to just use this store need only pay attention to
32: * the constructor.
33: *
34: * @package OpenID
35: */
36: class Auth_OpenID_MemcachedStore extends Auth_OpenID_OpenIDStore {
37:
38: /**
39: * Initializes a new {@link Auth_OpenID_MemcachedStore} instance.
40: * Just saves memcached object as property.
41: *
42: * @param resource connection Memcache connection resourse
43: */
44: function Auth_OpenID_MemcachedStore($connection, $compress = false)
45: {
46: $this->connection = $connection;
47: $this->compress = $compress ? MEMCACHE_COMPRESSED : 0;
48: }
49:
50: /**
51: * Store association until its expiration time in memcached.
52: * Overwrites any existing association with same server_url and
53: * handle. Handles list of associations for every server.
54: */
55: function storeAssociation($server_url, $association)
56: {
57: // create memcached keys for association itself
58: // and list of associations for this server
59: $associationKey = $this->associationKey($server_url,
60: $association->handle);
61: $serverKey = $this->associationServerKey($server_url);
62:
63: // get list of associations
64: $serverAssociations = $this->connection->get($serverKey);
65:
66: // if no such list, initialize it with empty array
67: if (!$serverAssociations) {
68: $serverAssociations = array();
69: }
70: // and store given association key in it
71: $serverAssociations[$association->issued] = $associationKey;
72:
73: // save associations' keys list
74: $this->connection->set(
75: $serverKey,
76: $serverAssociations,
77: $this->compress
78: );
79: // save association itself
80: $this->connection->set(
81: $associationKey,
82: $association,
83: $this->compress,
84: $association->issued + $association->lifetime);
85: }
86:
87: /**
88: * Read association from memcached. If no handle given
89: * and multiple associations found, returns latest issued
90: */
91: function getAssociation($server_url, $handle = null)
92: {
93: // simple case: handle given
94: if ($handle !== null) {
95: // get association, return null if failed
96: $association = $this->connection->get(
97: $this->associationKey($server_url, $handle));
98: return $association ? $association : null;
99: }
100:
101: // no handle given, working with list
102: // create key for list of associations
103: $serverKey = $this->associationServerKey($server_url);
104:
105: // get list of associations
106: $serverAssociations = $this->connection->get($serverKey);
107: // return null if failed or got empty list
108: if (!$serverAssociations) {
109: return null;
110: }
111:
112: // get key of most recently issued association
113: $keys = array_keys($serverAssociations);
114: sort($keys);
115: $lastKey = $serverAssociations[array_pop($keys)];
116:
117: // get association, return null if failed
118: $association = $this->connection->get($lastKey);
119: return $association ? $association : null;
120: }
121:
122: /**
123: * Immediately delete association from memcache.
124: */
125: function removeAssociation($server_url, $handle)
126: {
127: // create memcached keys for association itself
128: // and list of associations for this server
129: $serverKey = $this->associationServerKey($server_url);
130: $associationKey = $this->associationKey($server_url,
131: $handle);
132:
133: // get list of associations
134: $serverAssociations = $this->connection->get($serverKey);
135: // return null if failed or got empty list
136: if (!$serverAssociations) {
137: return false;
138: }
139:
140: // ensure that given association key exists in list
141: $serverAssociations = array_flip($serverAssociations);
142: if (!array_key_exists($associationKey, $serverAssociations)) {
143: return false;
144: }
145:
146: // remove given association key from list
147: unset($serverAssociations[$associationKey]);
148: $serverAssociations = array_flip($serverAssociations);
149:
150: // save updated list
151: $this->connection->set(
152: $serverKey,
153: $serverAssociations,
154: $this->compress
155: );
156:
157: // delete association
158: return $this->connection->delete($associationKey);
159: }
160:
161: /**
162: * Create nonce for server and salt, expiring after
163: * $Auth_OpenID_SKEW seconds.
164: */
165: function useNonce($server_url, $timestamp, $salt)
166: {
167: global $Auth_OpenID_SKEW;
168:
169: // save one request to memcache when nonce obviously expired
170: if (abs($timestamp - time()) > $Auth_OpenID_SKEW) {
171: return false;
172: }
173:
174: // returns false when nonce already exists
175: // otherwise adds nonce
176: return $this->connection->add(
177: 'openid_nonce_' . sha1($server_url) . '_' . sha1($salt),
178: 1, // any value here
179: $this->compress,
180: $Auth_OpenID_SKEW);
181: }
182:
183: /**
184: * Memcache key is prefixed with 'openid_association_' string.
185: */
186: function associationKey($server_url, $handle = null)
187: {
188: return 'openid_association_' . sha1($server_url) . '_' . sha1($handle);
189: }
190:
191: /**
192: * Memcache key is prefixed with 'openid_association_' string.
193: */
194: function associationServerKey($server_url)
195: {
196: return 'openid_association_server_' . sha1($server_url);
197: }
198:
199: /**
200: * Report that this storage doesn't support cleanup
201: */
202: function supportsCleanup()
203: {
204: return false;
205: }
206: }
207:
208: