Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added config_db_migrate/__init__.py
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""Share sessions through the database

Revision ID: 150800b30447
Revises: 8268fc7ca7f4
Create Date: 2017-11-23 15:26:45.594141

"""

# revision identifiers, used by Alembic.
revision = '150800b30447'
down_revision = '8268fc7ca7f4'
branch_labels = None
depends_on = None

from alembic import op
import sqlalchemy as sa


def upgrade():
op.create_table('sessions',
sa.Column('auth_string', sa.CHAR(64), nullable=False),
sa.Column('token', sa.CHAR(32), nullable=False),
sa.Column('last_access', sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint('auth_string',
name=op.f('pk_sessions'))
)


def downgrade():
op.drop_table('sessions')
Empty file.
8 changes: 4 additions & 4 deletions libcodechecker/libclient/authentication_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
# -------------------------------------------------------------------------

import os
import sys
# import datetime
import socket
import sys

from thrift.transport import THttpClient
from thrift.protocol import TJSONProtocol
Expand All @@ -17,9 +17,10 @@
import shared
from Authentication_v6 import codeCheckerAuthentication

from libcodechecker import session_manager
from libcodechecker import util

from credential_manager import SESSION_COOKIE_NAME


class ThriftAuthHelper():
def __init__(self, protocol, host, port, uri,
Expand All @@ -32,8 +33,7 @@ def __init__(self, protocol, host, port, uri,
self.client = codeCheckerAuthentication.Client(self.protocol)

if session_token:
headers = {'Cookie': session_manager.SESSION_COOKIE_NAME +
"=" + session_token}
headers = {'Cookie': SESSION_COOKIE_NAME + '=' + session_token}
self.transport.setCustomHeaders(headers)

# ------------------------------------------------------------
Expand Down
22 changes: 11 additions & 11 deletions libcodechecker/libclient/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
import shared
from Authentication_v6 import ttypes as AuthTypes

from libcodechecker import session_manager
from libcodechecker.logger import get_logger
from libcodechecker.util import split_product_url
from libcodechecker.version import CLIENT_API

from . import authentication_helper
from . import thrift_helper
from . import product_helper
from . import thrift_helper
from credential_manager import UserCredentials

LOG = get_logger('system')

Expand All @@ -46,8 +46,8 @@ def setup_auth_client(protocol, host, port, session_token=None):
"""

if not session_token:
manager = session_manager.SessionManager_Client()
session_token = manager.getToken(host, port)
manager = UserCredentials()
session_token = manager.get_token(host, port)
session_token_new = perform_auth_for_handler(protocol,
manager, host,
port,
Expand Down Expand Up @@ -79,8 +79,8 @@ def setup_auth_client_from_url(product_url, session_token=None):


def handle_auth(protocol, host, port, username, login=False):
session = session_manager.SessionManager_Client()
auth_token = session.getToken(host, port)
session = UserCredentials()
auth_token = session.get_token(host, port)
auth_client = authentication_helper.ThriftAuthHelper(protocol, host,
port,
'/v' +
Expand All @@ -92,7 +92,7 @@ def handle_auth(protocol, host, port, username, login=False):
if not login:
logout_done = auth_client.destroySession()
if logout_done:
session.saveToken(host, port, None, True)
session.save_token(host, port, None, True)
LOG.info("Successfully logged out.")
return

Expand All @@ -116,7 +116,7 @@ def handle_auth(protocol, host, port, username, login=False):
if 'Username:Password' in str(methods):

# Try to use a previously saved credential from configuration file.
saved_auth = session.getAuthString(host, port)
saved_auth = session.get_auth_string(host, port)

if saved_auth:
LOG.info("Logging in using preconfigured credentials...")
Expand All @@ -134,7 +134,7 @@ def handle_auth(protocol, host, port, username, login=False):
username + ":" +
pwd)

session.saveToken(host, port, session_token)
session.save_token(host, port, session_token)
LOG.info("Server reported successful authentication.")
except shared.ttypes.RequestFailed as reqfail:
LOG.error("Authentication failed! Please check your credentials.")
Expand Down Expand Up @@ -170,15 +170,15 @@ def perform_auth_for_handler(protocol, manager, host, port,
print_err = False

if manager.is_autologin_enabled():
auto_auth_string = manager.getAuthString(host, port)
auto_auth_string = manager.get_auth_string(host, port)
if auto_auth_string:
# Try to automatically log in with a saved credential
# if it exists for the server.
try:
session_token = auth_client.performLogin(
"Username:Password",
auto_auth_string)
manager.saveToken(host, port, session_token)
manager.save_token(host, port, session_token)
LOG.info("Authenticated using pre-configured "
"credentials.")
return session_token
Expand Down
92 changes: 92 additions & 0 deletions libcodechecker/libclient/credential_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# -------------------------------------------------------------------------
# The CodeChecker Infrastructure
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
# -------------------------------------------------------------------------
"""
Handles the management of stored user credentials and currently known session
tokens.
"""

import json
import os
import stat

import portalocker

from libcodechecker.logger import get_logger
from libcodechecker.util import check_file_owner_rw
from libcodechecker.util import load_json_or_empty
from libcodechecker.version import SESSION_COOKIE_NAME as _SCN

LOG = get_logger('system')
SESSION_COOKIE_NAME = _SCN


class UserCredentials:

def __init__(self):
LOG.debug("Loading clientside session config.")

# Check whether user's configuration exists.
user_home = os.path.expanduser("~")
session_cfg_file = os.path.join(user_home,
".codechecker.passwords.json")
LOG.debug(session_cfg_file)

scfg_dict = load_json_or_empty(session_cfg_file, {},
"user authentication")
if os.path.exists(session_cfg_file):
check_file_owner_rw(session_cfg_file)

if not scfg_dict.get('credentials'):
scfg_dict['credentials'] = {}

self.__save = scfg_dict
self.__autologin = scfg_dict.get('client_autologin', True)

# Check and load token storage for user.
self.token_file = os.path.join(user_home, ".codechecker.session.json")
LOG.debug(self.token_file)

if os.path.exists(self.token_file):
token_dict = load_json_or_empty(self.token_file, {},
"user authentication")
check_file_owner_rw(self.token_file)

self.__tokens = token_dict.get('tokens')
else:
with open(self.token_file, 'w') as f:
json.dump({'tokens': {}}, f)
os.chmod(self.token_file, stat.S_IRUSR | stat.S_IWUSR)

self.__tokens = {}

def is_autologin_enabled(self):
return self.__autologin

def get_token(self, host, port):
return self.__tokens.get("{0}:{1}".format(host, port))

def get_auth_string(self, host, port):
ret = self.__save['credentials'].get('{0}:{1}'.format(host, port))
if not ret:
ret = self.__save['credentials'].get(host)
if not ret:
ret = self.__save['credentials'].get('*:{0}'.format(port))
if not ret:
ret = self.__save['credentials'].get('*')

return ret

def save_token(self, host, port, token, destroy=False):
if destroy:
del self.__tokens['{0}:{1}'.format(host, port)]
else:
self.__tokens['{0}:{1}'.format(host, port)] = token

with open(self.token_file, 'w') as scfg:
portalocker.lock(scfg, portalocker.LOCK_EX)
json.dump({'tokens': self.__tokens}, scfg,
indent=2, sort_keys=True)
portalocker.unlock(scfg)
6 changes: 3 additions & 3 deletions libcodechecker/libclient/product_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
import shared
from ProductManagement_v6 import codeCheckerProductService

from libcodechecker import session_manager
from libcodechecker import util

from credential_manager import SESSION_COOKIE_NAME


class ThriftProductHelper(object):
def __init__(self, protocol, host, port, uri, session_token=None):
Expand All @@ -30,8 +31,7 @@ def __init__(self, protocol, host, port, uri, session_token=None):
self.client = codeCheckerProductService.Client(self.protocol)

if session_token:
headers = {'Cookie': session_manager.SESSION_COOKIE_NAME +
"=" + session_token}
headers = {'Cookie': SESSION_COOKIE_NAME + '=' + session_token}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You use this line of code in multiple places. Can we create a helper function for it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there exists a good place where this helper function could be put.

self.transport.setCustomHeaders(headers)

# ------------------------------------------------------------
Expand Down
5 changes: 2 additions & 3 deletions libcodechecker/libclient/thrift_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
import shared
from codeCheckerDBAccess_v6 import codeCheckerDBAccess

from libcodechecker import session_manager
from libcodechecker import util
from libcodechecker.logger import get_logger

from credential_manager import SESSION_COOKIE_NAME

LOG = get_logger('system')

Expand All @@ -35,8 +35,7 @@ def __init__(self, protocol, host, port, uri, session_token=None):
self.client = codeCheckerDBAccess.Client(self.protocol)

if session_token:
headers = {'Cookie': session_manager.SESSION_COOKIE_NAME +
"=" + session_token}
headers = {'Cookie': SESSION_COOKIE_NAME + '=' + session_token}
self.transport.setCustomHeaders(headers)

def ThriftClientCall(function):
Expand Down
7 changes: 2 additions & 5 deletions libcodechecker/libhandlers/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,10 @@
from libcodechecker import host_check
from libcodechecker import logger
from libcodechecker import output_formatters
from libcodechecker import session_manager
from libcodechecker import util
from libcodechecker.analyze import analyzer_env
from libcodechecker.server import server
from libcodechecker.server import instance_manager
from libcodechecker.server import server
from libcodechecker.server.database import database
from libcodechecker.server.database import database_status
from libcodechecker.server.database.config_db_model \
Expand Down Expand Up @@ -679,8 +678,6 @@ def server_init_start(args):

context = generic_package_context.get_context()
context.codechecker_workspace = args.config_directory
session_manager.SessionManager.CodeChecker_Workspace = \
args.config_directory
context.db_username = args.dbusername

check_env = analyzer_env.get_check_env(context.path_env_extra,
Expand Down Expand Up @@ -861,7 +858,7 @@ def server_init_start(args):
def main(args):
"""
Setup a logger server based on the configuration and
manage the Codechecker server.
manage the CodeChecker server.
"""
with logger.LOG_CFG_SERVER(args.verbose):
server_init_start(args)
2 changes: 1 addition & 1 deletion libcodechecker/server/api/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def getAuthParameters(self):
token = None
if self.__auth_session:
token = self.__auth_session.token
return HandshakeInformation(self.__manager.isEnabled(),
return HandshakeInformation(self.__manager.is_enabled,
self.__manager.is_valid(
token,
True))
Expand Down
16 changes: 16 additions & 0 deletions libcodechecker/server/database/config_db_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from __future__ import print_function
from __future__ import unicode_literals
from datetime import datetime
import sys

from sqlalchemy import *
Expand Down Expand Up @@ -107,6 +108,21 @@ def __init__(self, permission, product_id, name, is_group=False):
self.is_group = is_group


class Session(Base):
__tablename__ = 'sessions'

# Auth-String is a SHA-256 hash, while token is a Python UUID which is
# 32 characters (both in hex expanded format).
auth_string = Column(CHAR(64), nullable=False, primary_key=True)
token = Column(CHAR(32), nullable=False)
last_access = Column(DateTime, nullable=False)

def __init__(self, auth_string, token):
self.auth_string = auth_string
self.token = token
self.last_access = datetime.now()


IDENTIFIER = {
'identifier': "ConfigDatabase",
'orm_meta': CC_META,
Expand Down
Loading