Skip to content

Commit

Permalink
[web] Get access controls
Browse files Browse the repository at this point in the history
- Create a new global role (`PERMISSION_VIEW`) which will be used to
allow the users to fetch access control information from a running
CodeChecker server by using the `CodeChecker cmd permissions` subcommand.
- Add new test cases.
  • Loading branch information
csordasmarton committed Nov 5, 2021
1 parent 8c3bce4 commit 9ad34d6
Show file tree
Hide file tree
Showing 27 changed files with 570 additions and 41 deletions.
11 changes: 11 additions & 0 deletions docs/web/permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Table of Contents
* [Available permissions](#available-permissions)
* [Server-wide (global) permissions](#global-permissions)
* [`SUPERUSER`](#superuser)
* [`PERMISSION_VIEW`](#permission-view)
* [Product-level permissions](#product-level-permissions)
* [`PRODUCT_ADMIN`](#product-admin)
* [`PRODUCT_ACCESS`](#product-access)
Expand Down Expand Up @@ -130,6 +131,16 @@ system.
> immediately change everything on the server, from demoting other superusers
> to destroying analysis results!

### `PERMISSION_VIEW`

| Default | Managed by | Inherited from |
|---------|-----------------|----------------------------------|
| Granted | `SUPERUSER` | `SUPERUSER` |

Users with this permission can get information about access controls: which
user or group has global permissions or permissions only for specific products.
For more information check the `CodeChecker cmd permissions` command.

## Product-level permissions <a name="product-level-permissions"></a>

### `PRODUCT_ADMIN` <a name="product-admin"></a>
Expand Down
55 changes: 55 additions & 0 deletions docs/web/user_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Table of Contents
* [`suppress` (Manage and export/import suppressions)](#manage-suppressions)
* [Import suppressions between server and suppress file](#import-suppressions)
* [`products` (Manage product configuration of a server)](#cmd-product)
* [`permissions (Get access control)`](#get-access-control-permissions)
* [`login` (Authenticate to the server)](#cmd-login)
* [`export` (Export comments and review statuses from CodeChecker)](#cmd-export)
* [`import` (Import comments and review statuses into CodeChecker)](#cmd-import)
Expand Down Expand Up @@ -1623,6 +1624,60 @@ the database on the server.

Please see [Product management](products.md) for details.

### Get access control (`permissions`)
You can use this command to get access control information from a running
CodeChecker server. This will contain information which user or group has
global permissions or permissions only for specific products.

The output format of this command is the following:
```json
{
"version": 1,
"global_permissions": {
"user_permissions": {
"<user-name-1>": ["<permission-1>"]
},
"group_permissions": {
}
},
"product_permissions": {
"<product-name>": {
"user_permissions": {
"<user-name-2>": ["<permission-2>", "<permission-3>"]
},
"group_permissions": {
"<group-name-1>": ["<permission-3>"]
}
}
}
}
```

<details>
<summary>
<i>$ <b>CodeChecker cmd permissions --help</b> (click to expand)</i>
</summary>

```
usage: CodeChecker cmd permissions [-h] [-o {json}] [--url SERVER_URL]
[--verbose {info,debug_analyzer,debug}]
Get access control information from a CodeChecker server. PERMISSION_VIEW
access right is required to run this command.
optional arguments:
-h, --help show this help message and exit
-o {json}, --output {json}
The output format to use in showing the data.
(default: json)
common arguments:
--url SERVER_URL The URL of the server to access, in the format of
'[http[s]://]host:port'. (default: localhost:8001)
--verbose {info,debug_analyzer,debug}
Set verbosity level.
```

### Authenticate to the server (`login`) <a name="cmd-login"></a>
<details>
<summary>
Expand Down
14 changes: 14 additions & 0 deletions web/api/authentication.thrift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ struct SessionTokenData {
}
typedef list<SessionTokenData> SessionTokenDataList

struct Permissions {
1: map<string, list<string>> user,
2: map<string, list<string>> group,
}

struct AccessControl {
1: Permissions globalPermissions,
2: map<string, Permissions> productPermissions,
}

service codeCheckerAuthentication {

// This method is a dummy stub requiring no permissions. When a server is
Expand All @@ -50,6 +60,10 @@ service codeCheckerAuthentication {
// Retrieves a list of accepted authentication methods from the server.
list<string> getAcceptedAuthMethods(),

// PERMISSION: PERMISSION_VIEW
AccessControl getAccessControl()
throws (1: codechecker_api_shared.RequestFailed requestError),

// Handles creating a session token for the user.
string performLogin(1: string authMethod,
2: string authString)
Expand Down
11 changes: 6 additions & 5 deletions web/api/codechecker_api_shared.thrift
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ exception RequestFailed {
* { i64 productID }
*/
enum Permission {
SUPERUSER = 1, // scope: SYSTEM
SUPERUSER = 1, // scope: SYSTEM
PERMISSION_VIEW = 2, // scope: SYSTEM

PRODUCT_ADMIN = 16, // scope: PRODUCT
PRODUCT_ACCESS = 17, // scope: PRODUCT
PRODUCT_STORE = 18, // scope: PRODUCT
PRODUCT_VIEW = 19 // scope: PRODUCT
PRODUCT_ADMIN = 16, // scope: PRODUCT
PRODUCT_ACCESS = 17, // scope: PRODUCT
PRODUCT_STORE = 18, // scope: PRODUCT
PRODUCT_VIEW = 19, // scope: PRODUCT
}

/**
Expand Down
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion web/api/js/codechecker-api-node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codechecker-api",
"version": "6.45.0",
"version": "6.46.0",
"description": "Generated node.js compatible API stubs for CodeChecker server.",
"main": "lib",
"homepage": "https://github.com/Ericsson/codechecker",
Expand Down
Binary file modified web/api/py/codechecker_api/dist/codechecker_api.tar.gz
Binary file not shown.
2 changes: 1 addition & 1 deletion web/api/py/codechecker_api/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
with open('README.md', encoding='utf-8', errors="ignore") as f:
long_description = f.read()

api_version = '6.45.0'
api_version = '6.46.0'

setup(
name='codechecker_api',
Expand Down
Binary file not shown.
2 changes: 1 addition & 1 deletion web/api/py/codechecker_api_shared/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
with open('README.md', encoding='utf-8', errors="ignore") as f:
long_description = f.read()

api_version = '6.45.0'
api_version = '6.46.0'

setup(
name='codechecker_api_shared',
Expand Down
16 changes: 16 additions & 0 deletions web/client/codechecker_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,22 @@ def check_preconfigured_username(username, host, port):
sys.exit(1)


def init_auth_client(protocol, host, port):
""" Setup a new auth client. """
auth_client = setup_auth_client(protocol, host, port)

# Check if local token is available.
cred_manager = UserCredentials()
session_token = cred_manager.get_token(host, port)

if not session_token:
LOG.info("No valid token or session was found for %s:%s", host, port)
session_token = perform_auth_for_handler(auth_client, host, port,
cred_manager)

return setup_auth_client(protocol, host, port, session_token)


def setup_auth_client(protocol, host, port, session_token=None):
"""
Setup the Thrift authentication client. Returns the client object and the
Expand Down
27 changes: 26 additions & 1 deletion web/client/codechecker_client/cmd/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@

from codechecker_client import cmd_line_client
from codechecker_client import product_client
from codechecker_client import source_component_client, token_client
from codechecker_client import permission_client, source_component_client, \
token_client

from codechecker_common import arg, logger, util
from codechecker_common.output import USER_FORMATS
Expand Down Expand Up @@ -1200,6 +1201,18 @@ def __register_importer(parser):
"the database.")


def __register_permissions(parser):
"""
Add argparse subcommand parser for the "handle permissions" action.
"""
parser.add_argument('-o', '--output',
dest="output_format",
required=False,
default="json",
choices=['json'],
help="The output format to use in showing the data.")


def __register_token(parser):
"""
Add argparse subcommand parser for the "handle token" action.
Expand Down Expand Up @@ -1499,5 +1512,17 @@ def add_arguments_to_parser(parser):
importer.set_defaults(func=cmd_line_client.handle_import)
__add_common_arguments(importer)

permissions = subcommands.add_parser(
'permissions',
formatter_class=arg.RawDescriptionDefaultHelpFormatter,
description="Get access control information from a CodeChecker "
"server. PERMISSION_VIEW access right is required to run "
"this command.",
help="Get access control information from a CodeChecker server."
)
__register_permissions(permissions)
permissions.set_defaults(func=permission_client.handle_permissions)
__add_common_arguments(permissions, needs_product_url=False)

# 'cmd' does not have a main() method in itself, as individual subcommands are
# handled later on separately.
4 changes: 4 additions & 0 deletions web/client/codechecker_client/helpers/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ def getAuthParameters(self):
def getAcceptedAuthMethods(self):
pass

@ThriftClientCall
def getAccessControl(self):
pass

@ThriftClientCall
def performLogin(self, auth_method, auth_string):
pass
Expand Down
70 changes: 70 additions & 0 deletions web/client/codechecker_client/permission_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# -------------------------------------------------------------------------
#
# Part of the CodeChecker project, under the Apache License v2.0 with
# LLVM Exceptions. See LICENSE for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
# -------------------------------------------------------------------------
"""
Argument handlers for the 'CodeChecker cmd permissions' subcommands.
"""

from typing import Dict

from codechecker_api.Authentication_v6.ttypes import AccessControl

from codechecker_common import logger

from .client import init_auth_client
from .cmd_line import CmdLineOutputEncoder
from .product import split_server_url

# Needs to be set in the handler functions.
LOG = None


def init_logger(level, stream=None, logger_name='system'):
logger.setup_logger(level, stream)
global LOG
LOG = logger.get_logger(logger_name)


def __convert_permissions(permissions: AccessControl) -> Dict:
""" Convert the given permissions to dictionary. """
ret = {"user_permissions": {}, "group_permissions": {}}

for user_name, perms in permissions.user.items():
ret["user_permissions"][user_name] = perms

for group_name, perms in permissions.group.items():
ret["group_permissions"][group_name] = perms

return ret


def handle_permissions(args):
"""
Argument handler for the 'CodeChecker cmd permissions' subcommand
"""
# If the given output format is not 'table', redirect logger's output to
# the stderr.
stream = None
if 'output_format' in args:
stream = 'stderr'

init_logger(args.verbose if 'verbose' in args else None, stream)

protocol, host, port = split_server_url(args.server_url)
client = init_auth_client(protocol, host, port)
access_control = client.getAccessControl()

if args.output_format == 'json':
print(CmdLineOutputEncoder().encode({
'version': 1,
'global_permissions': __convert_permissions(
access_control.globalPermissions),
'product_permissions': {
name: __convert_permissions(val)
for name, val in access_control.productPermissions.items()
},
}))
20 changes: 1 addition & 19 deletions web/client/codechecker_client/token_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@
Argument handlers for the 'CodeChecker cmd token' subcommands.
"""


from codechecker_common import logger
from codechecker_common.output import twodim

from .client import setup_auth_client, perform_auth_for_handler
from .client import init_auth_client
from .cmd_line import CmdLineOutputEncoder
from .credential_manager import UserCredentials
from .product import split_server_url

# Needs to be set in the handler functions.
Expand All @@ -28,22 +26,6 @@ def init_logger(level, stream=None, logger_name='system'):
LOG = logger.get_logger(logger_name)


def init_auth_client(protocol, host, port):
""" Setup a new auth client. """
auth_client = setup_auth_client(protocol, host, port)

# Check if local token is available.
cred_manager = UserCredentials()
session_token = cred_manager.get_token(host, port)

if not session_token:
LOG.info("No valid token or session was found for %s:%s", host, port)
session_token = perform_auth_for_handler(auth_client, host, port,
cred_manager)

return setup_auth_client(protocol, host, port, session_token)


def handle_add_token(args):
"""
Creates a new personal access token for the logged in user based on the
Expand Down
2 changes: 1 addition & 1 deletion web/codechecker_web/shared/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# The newest supported minor version (value) for each supported major version
# (key) in this particular build.
SUPPORTED_VERSIONS = {
6: 45
6: 46
}

# Used by the client to automatically identify the latest major and minor
Expand Down
Loading

0 comments on commit 9ad34d6

Please sign in to comment.