Statistics
| Branch: | Tag: | Revision:

root / lib / serializer.py @ d357f531

History | View | Annotate | Download (3.7 kB)

1
#
2
#
3

    
4
# Copyright (C) 2007, 2008 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21
"""Serializer abstraction module
22

23
This module introduces a simple abstraction over the serialization
24
backend (currently json).
25

26
"""
27

    
28
import simplejson
29
import re
30
import hmac
31

    
32
from ganeti import errors
33

    
34
try:
35
  from hashlib import sha1
36
except ImportError:
37
  import sha as sha1
38

    
39

    
40
_JSON_INDENT = 2
41

    
42
_RE_EOLSP = re.compile('[ \t]+$', re.MULTILINE)
43

    
44

    
45
def _GetJsonDumpers():
46
  """Returns two JSON functions to serialize data.
47

48
  @rtype: (callable, callable)
49
  @return: The function to generate a compact form of JSON and another one to
50
           generate a more readable, indented form of JSON (if supported)
51

52
  """
53
  plain_dump = simplejson.dumps
54

    
55
  # Check whether the simplejson module supports indentation
56
  try:
57
    simplejson.dumps(1, indent=_JSON_INDENT)
58
  except TypeError:
59
    # Indentation not supported
60
    indent_dump = plain_dump
61
  else:
62
    # Indentation supported
63
    indent_dump = lambda data: simplejson.dumps(data, indent=_JSON_INDENT)
64

    
65
  assert callable(plain_dump)
66
  assert callable(indent_dump)
67

    
68
  return (plain_dump, indent_dump)
69

    
70

    
71
(_DumpJson, _DumpJsonIndent) = _GetJsonDumpers()
72

    
73

    
74
def DumpJson(data, indent=True):
75
  """Serialize a given object.
76

77
  @param data: the data to serialize
78
  @param indent: whether to indent output (depends on simplejson version)
79

80
  @return: the string representation of data
81

82
  """
83
  if indent:
84
    fn = _DumpJsonIndent
85
  else:
86
    fn = _DumpJson
87

    
88
  txt = _RE_EOLSP.sub("", fn(data))
89
  if not txt.endswith('\n'):
90
    txt += '\n'
91

    
92
  return txt
93

    
94

    
95
def LoadJson(txt):
96
  """Unserialize data from a string.
97

98
  @param txt: the json-encoded form
99

100
  @return: the original data
101

102
  """
103
  return simplejson.loads(txt)
104

    
105

    
106
def DumpSignedJson(data, key, salt=None):
107
  """Serialize a given object and authenticate it.
108

109
  @param data: the data to serialize
110
  @param key: shared hmac key
111
  @return: the string representation of data signed by the hmac key
112

113
  """
114
  txt = DumpJson(data, indent=False)
115
  if salt is None:
116
    salt = ''
117
  signed_dict = {
118
    'msg': txt,
119
    'salt': salt,
120
    'hmac': hmac.new(key, salt + txt, sha1).hexdigest(),
121
  }
122
  return DumpJson(signed_dict, indent=False)
123

    
124

    
125
def LoadSignedJson(txt, key):
126
  """Verify that a given message was signed with the given key, and load it.
127

128
  @param txt: json-encoded hmac-signed message
129
  @param key: shared hmac key
130
  @rtype: tuple of original data, string
131
  @return: original data, salt
132
  @raises errors.SignatureError: if the message signature doesn't verify
133

134
  """
135
  signed_dict = LoadJson(txt)
136
  if not isinstance(signed_dict, dict):
137
    raise errors.SignatureError('Invalid external message')
138
  try:
139
    msg = signed_dict['msg']
140
    salt = signed_dict['salt']
141
    hmac_sign = signed_dict['hmac']
142
  except KeyError:
143
    raise errors.SignatureError('Invalid external message')
144

    
145
  if hmac.new(key, salt + msg, sha1).hexdigest() != hmac_sign:
146
    raise errors.SignatureError('Invalid Signature')
147

    
148
  return LoadJson(msg), salt
149

    
150

    
151
Dump = DumpJson
152
Load = LoadJson
153
DumpSigned = DumpSignedJson
154
LoadSigned = LoadSignedJson