def is_local(self, user): """ Check if given user is local or not :param User user: User to check :returns: True if user is a local user, otherwise False """ from multiproject.core.authentication import CQDEAuthenticationStore auth_store = CQDEAuthenticationStore.instance() return auth_store.is_local(user.authentication_key)
def __init__(self): self.auth_store = CQDEAuthenticationStore.instance() self.local_authentication_key = self.auth_store.get_authentication_id(self.LOCAL) if not self.local_authentication_key: self.auth_store.create_authentication(self.LOCAL) self.local_authentication_key = self.auth_store.get_authentication_id(self.LOCAL) if not self.local_authentication_key: # This should not happen raise Exception('LocalAuthentication: Could not get authentication id for LocalDB')
def _init_known_authentications(self): """ Lazy load authentications and store them locally to reduce sql """ auth_store = CQDEAuthenticationStore.instance() auths = auth_store.get_authentications() for auth in auths: self.authentications[auth.id] = auth.name self.authentications[None] = '<No authentication>'
def _init_known_authentications(self): """ Lazy load authentications and store them locally to reduce sql """ auth_store = CQDEAuthenticationStore.instance() auths = auth_store.get_authentications() for auth in auths: self.authentications[auth.id] = auth.name self.authentications[None] = '<No authentication>'
def __init__(self): self.org_store = CQDEOrganizationStore.instance() self.auth_store = CQDEAuthenticationStore.instance() self.ldap_authentication_key = self.auth_store.get_authentication_id(self.LDAP) if not self.ldap_authentication_key: self.auth_store.create_authentication(self.LDAP) self.ldap_authentication_key = self.auth_store.get_authentication_id(self.LDAP) if not self.ldap_authentication_key: # This should not happen raise Exception('LdapAuthentication: Could not get authentication id for LDAP')
def __init__(self): self.org_store = CQDEOrganizationStore.instance() self.auth_store = CQDEAuthenticationStore.instance() self.ldap_authentication_key = self.auth_store.get_authentication_id( self.LDAP) if not self.ldap_authentication_key: self.auth_store.create_authentication(self.LDAP) self.ldap_authentication_key = self.auth_store.get_authentication_id( self.LDAP) if not self.ldap_authentication_key: # This should not happen raise Exception( 'LdapAuthentication: Could not get authentication id for LDAP')
def upgrade(self): if self.applied(): print "Migration already applied".rjust(12) return True auth_store = CQDEAuthenticationStore.instance() id = auth_store.get_authentication_id(auth_store.LOCAL) if not id: print "Error: local authentication id not found: name is %s" % auth_store.LOCAL return False queries = [""" UPDATE `user` SET SHA1_PW = 'invalidNonLocalUserPwHash' WHERE authentication_key <> %s """ % safe_int(id)] return self.manager.db_upgrade(queries)
def applied(self): """ Check if column exists or not :returns: True if exists, otherwise False """ if self._pretend_not_applied: return False count = 0 auth_store = CQDEAuthenticationStore.instance() id = auth_store.get_authentication_id(auth_store.LOCAL) with admin_query() as cursor: cursor.execute(""" SELECT COUNT(*) AS count FROM `user` WHERE SHA1_PW <> 'invalidNonLocalUserPwHash' AND authentication_key <> %s """, (id,)) count = int(cursor.fetchone()[0]) return count == 0
def upgrade(self): if self.applied(): print "Migration already applied".rjust(12) return True auth_store = CQDEAuthenticationStore.instance() id = auth_store.get_authentication_id(auth_store.LOCAL) if not id: print "Error: local authentication id not found: name is %s" % auth_store.LOCAL return False queries = [ """ UPDATE `user` SET SHA1_PW = 'invalidNonLocalUserPwHash' WHERE authentication_key <> %s """ % safe_int(id) ] return self.manager.db_upgrade(queries)
def invalidate_user_password(self, user): """ Changes the user password to 'invalidNonLocalUserPwHash' for non-local user Clears caching as in updatePassword .. Note :: Used only for non-local users! SHA1_PW must not contain space characters, since it is used in memcache key """ self.__cache.clear_user_by_user(user) if not user.id: return False from multiproject.core.authentication import CQDEAuthenticationStore auth_store = CQDEAuthenticationStore.instance() query = """UPDATE `user` INNER JOIN authentication ON `user`.authentication_key = authentication.id SET `user`.SHA1_PW = 'invalidNonLocalUserPwHash' WHERE user_id = %s AND authentication.method <> %s""" result = True # Do invalidation with admin_transaction() as cursor: try: cursor.execute("SELECT SHA1_PW FROM user WHERE user_id = %s", user.id) sha = cursor.fetchone() self.__authcache.clearAuthentication(user.username, str(sha[0]).encode('utf-8')) if sha[0] != 'invalidNonLocalUserPwHash': # If not already invalid password hash cursor.execute(query, (user.id, auth_store.LOCAL)) if not cursor.rowcount: conf.log.error("Nothing affected when invalidating password for user %s" % user.username) result = False except Exception: conf.log.exception("Failed to invalidate password for user %s." % user.username) return False return result
def applied(self): """ Check if column exists or not :returns: True if exists, otherwise False """ if self._pretend_not_applied: return False count = 0 auth_store = CQDEAuthenticationStore.instance() id = auth_store.get_authentication_id(auth_store.LOCAL) with admin_query() as cursor: cursor.execute( """ SELECT COUNT(*) AS count FROM `user` WHERE SHA1_PW <> 'invalidNonLocalUserPwHash' AND authentication_key <> %s """, (id, )) count = int(cursor.fetchone()[0]) return count == 0
def render_admin_panel(self, req, cat, page, path_info): """ Renders admin panel and handles new user creation request """ req.perm.require('USER_CREATE') now = datetime.utcnow() expires = now + timedelta(days=90) data = { 'dateformats': DATEFORMATS, 'now': now, 'expires': expires, } # Helper class add_script(req, 'multiproject/js/multiproject.js') add_script(req, 'multiproject/js/admin_user_create.js') # Get and set option goto address if 'goto' in req.args: req.session['goto'] = conf.safe_address(req.args.get('goto', '')) req.session.save() # Create new user to local database if req.method.upper() == 'GET': return 'admin_user_create.html', data elif req.method.upper() == 'POST': userstore = get_userstore() user = self._get_user(req) author = userstore.getUser(req.authname) # Update data for pre-filled form data['username'] = user.username data['first'] = user.givenName data['last'] = user.lastName data['mail'] = user.mail data['mobile'] = user.mobile # Validate and set author if not req.perm.has_permission('USER_AUTHOR') or not author: chrome.add_warning( req, _("User needs to have author with USER_AUTHOR permission")) return 'admin_user_create.html', data user.author_id = author.id user.expires = expires org_store = CQDEOrganizationStore.instance() auth_store = CQDEAuthenticationStore.instance() user.authentication_key = auth_store.get_authentication_id( LocalAuthentication.LOCAL) user.organization_keys = org_store.get_organization_keys( user, LocalAuthentication.LOCAL) or None # Validate user object error_msg = self.validate_user(req, user) if error_msg: chrome.add_warning(req, error_msg) return 'admin_user_create.html', data # Try to store user if userstore.storeUser(user): userlink = tag.a(user.username, href=req.href('admin/users/manage', username=user.username)) chrome.add_notice(req, tag(_('Created new local user: '******'Created new local user "%s" by "%s"' % (user.username, req.authname)) # Try to send email notification also try: self._send_notification(user, req.server_name) except TracError: # Notification sending failed self.log.exception("Notification sending failed") chrome.add_warning(req, _('Failed to send email notification')) # Handle optional goto argument if 'goto' in req.session: goto = req.session['goto'] del req.session['goto'] # NOTE: Show redirect address as a system message instead of direct redirection # This is because after moving to another project, the system messages are not shown due the separate # sessions per project chrome.add_notice( req, Markup('Go back to: <a href="%s">%s</a>' % (goto, goto))) # Redirect to the page so that we're not showing the created user form with prefilled return req.redirect(req.href('admin/users/create_local')) return 'admin_user_create.html', data
def render_admin_panel(self, req, cat, page, path_info): """ Renders admin panel and handles new user creation request """ req.perm.require('USER_CREATE') now = datetime.utcnow() expires = now + timedelta(days=90) data = { 'dateformats':DATEFORMATS, 'now':now, 'expires':expires, } # Helper class add_script(req, 'multiproject/js/multiproject.js') add_script(req, 'multiproject/js/admin_user_create.js') # Get and set option goto address if 'goto' in req.args: req.session['goto'] = conf.safe_address(req.args.get('goto', '')) req.session.save() # Create new user to local database if req.method.upper() == 'GET': return 'admin_user_create.html', data elif req.method.upper() == 'POST': userstore = get_userstore() user = self._get_user(req) author = userstore.getUser(req.authname) # Update data for pre-filled form data['username'] = user.username data['first'] = user.givenName data['last'] = user.lastName data['mail'] = user.mail data['mobile'] = user.mobile # Validate and set author if not req.perm.has_permission('USER_AUTHOR') or not author: chrome.add_warning(req, _("User needs to have author with USER_AUTHOR permission")) return 'admin_user_create.html', data user.author_id = author.id user.expires = expires org_store = CQDEOrganizationStore.instance() auth_store = CQDEAuthenticationStore.instance() user.authentication_key = auth_store.get_authentication_id(LocalAuthentication.LOCAL) user.organization_keys = org_store.get_organization_keys(user, LocalAuthentication.LOCAL) or None # Validate user object error_msg = self.validate_user(req, user) if error_msg: chrome.add_warning(req, error_msg) return 'admin_user_create.html', data # Try to store user if userstore.storeUser(user): userlink = tag.a(user.username, href=req.href('admin/users/manage', username=user.username)) chrome.add_notice(req, tag(_('Created new local user: '******'Created new local user "%s" by "%s"' % (user.username, req.authname)) # Try to send email notification also try: self._send_notification(user) except TracError: # Notification sending failed self.log.exception("Notification sending failed") chrome.add_warning(req, _('Failed to send email notification')) # Handle optional goto argument if 'goto' in req.session: goto = req.session['goto'] del req.session['goto'] # NOTE: Show redirect address as a system message instead of direct redirection # This is because after moving to another project, the system messages are not shown due the separate # sessions per project chrome.add_notice(req, Markup('Go back to: <a href="%s">%s</a>' % (goto, goto))) # Redirect to the page so that we're not showing the created user form with prefilled return req.redirect(req.href('admin/users/create_local')) return 'admin_user_create.html', data
def __init__(self): self.auth_store = CQDEAuthenticationStore.instance() self.primary_auth_method = conf.authentication_order[0].lower()
def check_permission(self, trac_environment_id, permission, check_username): """ Helper class for the GlobalPermissionPolicy.check_permission, which checks also the resource, unlike this. Checks permission for the user in the trac environment. """ # Disable completely features by appending actions into this list restricted_features = ['REPORT_CREATE', 'REPORT_SQL_VIEW'] if permission in restricted_features: return False # Get user in question user = get_userstore().getUser(check_username) store = CQDEUserGroupStore(trac_environment_id) superusers = CQDESuperUserStore.instance() # If there is no user then there is no need for permission check if not user: return False # Check if user is a superuser if superusers.is_superuser(user.username): return True # Get all groups and users user_groups = store.get_all_user_groups() group_perms = store.get_all_group_permissions() organization_groups = store.get_all_organization_groups() # List groups that have the permission groups = [] for group, perm in group_perms: # NOTE: Also extend the meta permissions into list perms = [perm] + self.meta_perms.get(perm, []) if permission in perms: groups.append(group) users = get_special_users(user.username) users.append(user.username) # See if user is in one of the groups for username, group in user_groups: if username in users: if group in groups: return True # See if user's organization is in one of the groups org_store = CQDEOrganizationStore.instance() for org, group in organization_groups: org_id = org_store.get_organization_id(org) if org_id in user.organization_keys: if group in groups: return True if conf.ldap_groups_enabled: # TODO: do not use CQDEAuthenticationStore to check this, the information should # be available from User object (add if not!) # See if any ldap groups are allowed in environment from multiproject.core.authentication import CQDEAuthenticationStore auth_store = CQDEAuthenticationStore.instance() is_ldap_account = auth_store.is_ldap(user.authentication_key) trac_environment_ldapgroups = store.get_all_trac_environment_ldap_groups() if is_ldap_account and trac_environment_ldapgroups: # See if user belongs to any of the allowed ldap groups ldapuser_store = conf.getAuthenticationStore() user_ldapgroups = ldapuser_store.getGroups(user.username) for ldapgroup, group in trac_environment_ldapgroups: if ldapgroup in user_ldapgroups: if group in groups: return True return False
def __init__(self): self.auth_store = CQDEAuthenticationStore.instance() self.primary_auth_method = conf.authentication_order[0].lower()
def get_participated_projects(self, user, by_organization=False, by_ldap=False, public_only=False): """ Get those projects that user has participated. Optionally can list by organization, or by public status. :param User user: User object :param boolean by_organization: List projects by organization :param boolean public_only: Get public projects as well :returns: List of Project objects """ # Anonymous does not have organization if not user.organization_keys: by_organization = False and_anon_condition= '' union_all_organization_condition = '' union_all_ldap_condition = '' perm_ids = ', '.join([str(safe_int(get_permission_id(action))) for action in ('TEAM_VIEW',)]) if public_only: # fetch anonymous user id # FIXME: Would be nice if we didn't have to do this all the time anon = get_userstore().getUser('anonymous') if not anon: conf.log.warning("Error in get_participated_projects: No anonymous user obtained!") raise TracError("Error while fetching user's projects.") and_anon_condition = """ AND EXISTS (SELECT group.trac_environment_key FROM `group` INNER JOIN user_group ON user_group.group_key = group.group_id INNER JOIN group_permission ON group.group_id = group_permission.group_key WHERE user_group.user_key = {anon_id} AND group.trac_environment_key = projects.trac_environment_key AND group_permission.permission_key IN ({perm_ids}) ) """.format(anon_id = safe_int(anon.id), perm_ids = perm_ids) if by_organization: # See if the user has access into projects through organizations union_all_organization_condition += """ UNION ALL SELECT group.trac_environment_key FROM `trac_admin`.`group` INNER JOIN organization_group ON organization_group.group_key = group.group_id WHERE organization_group.organization_key IN({organization_ids}) """.format(organization_ids = ', '.join([str(safe_int(org_id)) for org_id in user.organization_keys])) if by_ldap and conf.ldap_groups_enabled: # See if any ldap groups are allowed in environment auth_store = CQDEAuthenticationStore.instance() is_ldap_account = auth_store.is_ldap(user.authentication_key) if is_ldap_account: # See if user belongs to any of the allowed ldap groups ldapuser_store = conf.getAuthenticationStore() user_ldapgroups = ldapuser_store.getGroups(user.username) if user_ldapgroups: union_all_ldap_condition = """ UNION ALL SELECT group.trac_environment_key FROM `trac_admin`.`group` INNER JOIN ldapgroup_group ON ldapgroup_group.group_key = group.group_id INNER JOIN ldapgroup ON ldapgroup.ldapgroup_id = ldapgroup_group.ldapgroup_key WHERE ldapgroup.ldapgroup_name IN ({ldapgroup_names}) """.format(ldapgroup_names = ', '.join(["'{0}'".format(safe_string(group)) for group in user_ldapgroups])) query = """ SELECT projects.* FROM projects WHERE projects.trac_environment_key IN ( SELECT group.trac_environment_key FROM `trac_admin`.`group` INNER JOIN user_group ON user_group.group_key = group.group_id WHERE user_group.user_key = {user_id} {union_all_organization} {union_all_ldap} ) {and_anon} ORDER BY projects.project_name ASC """.format(user_id = safe_int(user.id), union_all_organization = union_all_organization_condition, union_all_ldap = union_all_ldap_condition, and_anon = and_anon_condition) conf.log.debug("queried participated projects with %s" % query) projects = self.queryProjectObjects(query) return projects
def get_organization_keys(self, user, auth_method=None): """ Returns list of organization keys matching with the user (and optionally authentication method) Each authentication method has corresponding organization, which are applied to the user by default, when user is created :param User user: User to get organization keys for :param str auth_method: Name of the authentication method / backend. Valid values: LocalDB,LDAP, Example:: from multiproject.core.auth.local_auth import LocalAuthentication from multiproject.core.users import get_userstore userstore = get_userstore() user = userstore.getUser('name') get_organization_keys(user, LocalAuthentication.LOCAL) """ from multiproject.common.projects import HomeProject from multiproject.common.users import OrganizationManager organization_ids = [] # Load home env to load component self.env = HomeProject().get_env() orgman = self.env[OrganizationManager] if not orgman.use_organizations: return [] # If authentication method/backend is not defined, load info from user if not auth_method: # TODO: do not use CQDEAuthenticationStore to check this, the information should # be available from User object (add if not!) from multiproject.core.authentication import CQDEAuthenticationStore auth_store = CQDEAuthenticationStore.instance() auth_method = auth_store.get_authentication_method(user.authentication_key) if auth_method: try: # Iterate all organization names that matches with authentication backend name be_orgs = [org for org in orgman.get_organizations_by_backend(auth_method) if org['type'] == 'auth'] if not be_orgs: conf.log.info('Failed to find backend based organization info: %s' % auth_method) for org in be_orgs: id = int(self.get_organization_id(org['name'])) if id and id not in organization_ids: organization_ids.append(id) except: conf.log.exception("Exception on auth_method") # Check if mail based organizations are defined if user.mail: try: # Iterate all organization names that matches with email domain: @domain.com mailhost = "@" + user.mail.split("@")[1] mail_orgs = [org for org in orgman.get_organizations_by_backend(mailhost) if org['type'] == 'email'] if not mail_orgs: conf.log.info('Failed to find email based organization info: %s' % mailhost) for org in mail_orgs: id = int(self.get_organization_id(org['name'])) if id and id not in organization_ids: organization_ids.append(id) except: conf.log.exception("Failed to read email based organization info") conf.log.debug('Found organization ids %s for user %s' % (organization_ids, user)) return organization_ids
def storeUser(self, user): """ Function for writing user into persistent storage .. IMPORTANT:: Function also saves the password hash into database. If non-local user, pw hash will be 'invalidNonLocalUserPwHash'. :returns: True on success, False on failure """ self.__cache.clearUser(user.username) # Set defaults for optional fields user.mobile = user.mobile or '' user.password = user.password or '' user.givenName = user.givenName or '' user.lastName = user.lastName or '' user.icon = safe_int(user.icon) or None user.created = user.created or datetime.utcnow() user.expires = user.expires or None # By default, no expire date user.author_id = user.author_id or None # By default, no author/owner information # Check the required fields: username and password if not user.username: conf.log.error('User object is missing username - giving up user store') return False auth_store = CQDEAuthenticationStore.instance() local_authentication_key = auth_store.get_authentication_id(auth_store.LOCAL) if not user.authentication_key: user.authentication_key = local_authentication_key # Set default status id based on name defined configuration default_status_name = conf.default_user_status.lower() status_key = self.USER_STATUS_KEYS.get(default_status_name) if not status_key: self.log.error('Failed to find and set user default status') return False sha1_pw = 'SHA1(%s)' if user.authentication_key != local_authentication_key: user.password = '******' sha1_pw = '%s' # SQL query for creating a new user query = """ INSERT INTO user ( user_id, username, mail, mobile, givenname, lastname, created, icon_id, SHA1_PW, authentication_key, user_status_key, expires, author_id ) VALUES (NULL, %s, %s, %s, %s, %s, %s, %s, {sha1_pw}, %s, %s, %s, %s) """.format(sha1_pw=sha1_pw) # SQL params params = ( user.username, user.mail, user.mobile, user.givenName.encode('utf-8'), user.lastName.encode('utf-8'), user.created, user.icon, user.password.encode('utf-8'), str(user.authentication_key), str(status_key), user.expires, user.author_id ) with admin_transaction() as cursor: try: cursor.execute(query, params) user.id = cursor.lastrowid except: conf.log.exception("Failed to create user") raise self.storeUserOrganizations(user) self.updateUserPreferences(user) return True