The Assimilation Project  based on Assimilation version 1.1.7.1474836767
cmadb.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # vim: smartindent tabstop=4 shiftwidth=4 expandtab number colorcolumn=100
3 #
4 # This file is part of the Assimilation Project.
5 #
6 # Copyright (C) 2011, 2012 - Alan Robertson <alanr@unix.sh>
7 #
8 # The Assimilation software is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # The Assimilation software is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with the Assimilation Project software. If not, see http://www.gnu.org/licenses/
20 #
21 #
22 '''
23 This module defines our CMAdb class and so on...
24 '''
25 
26 import os, sys, random, subprocess
27 import getent
28 import py2neo
29 from store import Store
30 from AssimCtypes import NEO4JCREDFILENAME, CMAUSERID
31 
32 DEBUG = False
33 
34 # R0903: Too few public methods
35 # pylint: disable=R0903
36 class CMAdb(object):
37  '''Class defining our Neo4J database.'''
38  nodename = os.uname()[1]
39  debug = False
40  transaction = None
41  log = None
42  store = None
43  globaldomain = 'global'
44  underdocker = None
45  # versions we know we can't work with...
46  neo4jblacklist = ['2.0.0']
47 
48  def __init__(self, db=None):
49  self.db = db
50  CMAdb.store = Store(self.db, {}, {})
51  self.dbversion = self.db.neo4j_version
52  vers = ""
53  dot = ""
54  for elem in self.dbversion:
55  if str(elem) == '':
56  continue
57  vers += '%s%s' % (dot, str(elem))
58  dot = '.'
59  self.dbversstring = vers
60  if self.dbversstring in CMAdb.neo4jblacklist:
61  print >> sys.stderr, ("The Assimilation CMA isn't compatible with Neo4j version %s"
62  % self.dbversstring)
63  sys.exit(1)
64  self.nextlabelid = 0
65  if CMAdb.debug:
66  CMAdb.log.debug('Neo4j version: %s' % str(self.dbversion))
67  print >> sys.stderr, ('HELP Neo4j version: %s' % str(self.dbversion))
68 
69  @staticmethod
71  "Return True if we're running under docker - must be root the first time we're called"
72  if CMAdb.underdocker is None:
73  try:
74  initcmd = os.readlink("/proc/1/exe")
75  CMAdb.underdocker = (os.path.basename(initcmd) != 'init')
76  except OSError:
77  print >> sys.stderr, ('Assimilation needs to run --privileged under docker')
78  CMAdb.underdocker = True
79  return CMAdb.underdocker
80 
81 class Neo4jCreds(object):
82  'Neo4j credentials object'
83  default_name = 'neo4j' # Default "login" name
84  default_auth = 'neo4j' # built-in default password
85  default_length = 16 # default length of a randomly-generated password
86  passchange = 'neoauth' # Program to change passwords
87 
88  def __init__(self, filename=None, neologin=None, neopass=None):
89  '''Neoj4Creds constructor
90 
91  :arg filename location of where to find/stash the credentials (optional)
92  '''
93  if neologin is not None and neopass is not None:
94  self.isdefault = False
95  self.name=neologin
96  self.auth=neopass
97  return
98  if filename is None:
99  filename = NEO4JCREDFILENAME
100  self.filename = filename
101  self.dirname = os.path.dirname(self.filename)
102  self.isdefault = True
103  if (not os.access(self.dirname, os.W_OK|os.R_OK)):
104  raise IOError('Directory %s not accessible (are you root?)' % self.dirname)
105  try:
106  with open(self.filename) as f:
107  self.name=f.readline().strip()
108  self.auth=f.readline().strip()
109  self.isdefault = False
110  except IOError:
111  self.name = Neo4jCreds.default_name
112  self.auth = Neo4jCreds.default_auth
113 
114  @staticmethod
115  def randpass(length):
116  '''
117  Generate a random password from letters, digits and punctuation
118 
119  :param length: length of the password to generate
120  :return: password string
121  '''
122  chars = r'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' \
123  r'!@#$%^&*()_-+=|\~`{[}]:;,.<>/?'
124  ret = ''.join((random.choice(chars)) for _ in range(length))
125  return str(ret)
126 
127  def update(self, newauth=None, length=None):
128  '''Update credentials from the new authorization info we've been given.
129  '''
130  if length is None or length < 1:
131  length = Neo4jCreds.default_length
132  if (not os.access(self.dirname, os.W_OK)):
133  raise IOError('Directory %s not writable (are you root?)' % self.dirname)
134  if newauth is None:
135  newauth = Neo4jCreds.randpass(length)
136  if DEBUG:
137  print >> sys.stderr, 'Calling %s' % Neo4jCreds.passchange
138  rc = subprocess.check_call([Neo4jCreds.passchange, self.name, self.auth, newauth])
139  if rc != 0:
140  raise IOError('Cannot update neo4j credentials.')
141  self.auth = newauth
142  if DEBUG:
143  print >> sys.stderr, '%s "%s:%s" successful' % \
144  (Neo4jCreds.passchange, self.name, self.auth)
145  userinfo = getent.passwd(CMAUSERID)
146  if userinfo is None:
147  raise OSError('CMA user id "%s" is unknown' % CMAUSERID)
148  with open(self.filename, 'w') as f:
149  self.auth = newauth
150  os.chmod(self.filename, 0600)
151  # pylint is confused about getent.passwd...
152  # pylint: disable=E1101
153  os.chown(self.filename, userinfo.uid, userinfo.gid)
154  f.write('%s\n%s\n' % (self.name, self.auth))
155  print >> sys.stderr, 'Updated Neo4j credentials cached in %s.' % self.filename
156 
157  def authenticate(self, uri='localhost:7474'):
158  '''
159  Authenticate ourselves to the neo4j database using our credentials
160  '''
161  if self.isdefault:
162  self.update()
163  if DEBUG:
164  print >> sys.stderr, 'AUTH WITH ("%s")' % str(self)
165  py2neo.authenticate(uri, self.name, self.auth)
166 
167  def __str__(self, filename=None):
168  '''We return the current assimilation Neo4j credentials (login, password) as a string
169  :return: credentials tuple (login, password)
170  '''
171  return '%s:%s' % (self.name, self.auth)
172 
173 
174 if __name__ == '__main__':
175  # pylint: disable=C0413
176  from cmainit import CMAinit
177  print >> sys.stderr, 'Starting'
178  CMAinit(None, cleanoutdb=True, debug=True)
179  print >> sys.stderr, 'Init done'
def randpass(length)
Definition: cmadb.py:115
def __init__(self, filename=None, neologin=None, neopass=None)
Definition: cmadb.py:88
def update(self, newauth=None, length=None)
Definition: cmadb.py:127
def __init__(self, db=None)
Definition: cmadb.py:48
def __str__(self, filename=None)
Definition: cmadb.py:167
def authenticate(self, uri='localhost:7474')
Definition: cmadb.py:157
def running_under_docker()
Definition: cmadb.py:70