Skip to content

Commit

Permalink
add bearer auth / access token support (#77)
Browse files Browse the repository at this point in the history
* add bearer auth / access token support

* replace prefer-configured-key with prefer-configured-auth

* add changelog fragment

* replace use-galaxy-key with use-galaxy-auth

* update references for PREFER_CONFIGURED_AUTH

* replace publish-skip-configured-key with publish-skip-configured-auth

* fix text

* fix wrong option name

* change more references to [API] keys

* update readme - artifactory license requirements

* fix which option is deprecated

* add missing help text for galaxy-auth-type`

* update README usage

* update more key references

* fix wonky formatting
  • Loading branch information
briantist authored Aug 12, 2023
1 parent 04aa579 commit e458e5d
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 30 deletions.
48 changes: 37 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ It can also be set up to transparently proxy an upstream Galaxy server, storing
This project is _heavily_ inspired by [amanda](https://github.com/sivel/amanda/).

# Artifactory compatibility
For the time being some features may require an Artifactory Pro license. Work is being done to workaround the (many) limitations that JFrog has placed on API calls. [These limitations have also complicated the ability to run integration tests](https://github.com/briantist/galactory/issues/6). As a result, the test of whether all Pro license dependent API calls have been rooted out, will be whether test coverage exists that can run against Artifactory OSS.
All features of galactory should work with the free-of-cost Artifactory OSS. Please report any usage that appears to require a Pro license.

# How to use
There isn't any proper documentation yet. The help output is below.
Expand All @@ -33,9 +33,12 @@ usage: python -m galactory [-h] [-c CONFIG] [--listen-addr LISTEN_ADDR]
[--listen-port LISTEN_PORT] [--server-name SERVER_NAME]
[--preferred-url-scheme PREFERRED_URL_SCHEME]
--artifactory-path ARTIFACTORY_PATH
[--artifactory-api-key ARTIFACTORY_API_KEY] [--use-galaxy-key]
[--prefer-configured-key] [--publish-skip-configured-key]
[--log-file LOG_FILE]
[--artifactory-api-key ARTIFACTORY_API_KEY]
[--artifactory-access-token ARTIFACTORY_ACCESS_TOKEN]
[--use-galaxy-key] [--use-galaxy-auth]
[--galaxy-auth-type {api_key,access_token}] [--prefer-configured-key]
[--prefer-configured-auth] [--publish-skip-configured-key]
[--publish-skip-configured-auth] [--log-file LOG_FILE]
[--log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}] [--log-headers]
[--log-body] [--proxy-upstream PROXY_UPSTREAM]
[-npns NO_PROXY_NAMESPACE] [--cache-minutes CACHE_MINUTES]
Expand Down Expand Up @@ -65,16 +68,39 @@ optional arguments:
The URL of the path in Artifactory where collections are stored.
[env var: GALACTORY_ARTIFACTORY_PATH]
--artifactory-api-key ARTIFACTORY_API_KEY
If set, is the API key used to access Artifactory.
If set, is the API key used to access Artifactory. If set with artifactory-access-token, this
value will not be used.
[env var: GALACTORY_ARTIFACTORY_API_KEY]
--use-galaxy-key If set, uses the Galaxy token as the Artifactory API key.
--artifactory-access-token ARTIFACTORY_ACCESS_TOKEN
If set, is the Access Token used to access Artifactory. If set with artifactory-api-key, this
value will be used and the API key will be ignored.
[env var: GALACTORY_ARTIFACTORY_ACCESS_TOKEN]
--use-galaxy-key If set, uses the Galaxy token sent in the request as the Artifactory auth. DEPRECATED: This
option will be removed in v0.11.0. Please use --use-galaxy-auth going forward.
[env var: GALACTORY_USE_GALAXY_KEY]
--use-galaxy-auth If set, uses the Galaxy token sent in the request as the Artifactory auth.
[env var: GALACTORY_USE_GALAXY_AUTH]
--galaxy-auth-type {api_key,access_token}
Auth received via a Galaxy request should be interpreted as this type of auth.
[env var: GALACTORY_GALAXY_AUTH_TYPE]
--prefer-configured-key
If set, prefer the confgured Artifactory key over the Galaxy token.
If set, prefer the confgured Artifactory auth over the Galaxy token.
DEPRECATED: This option will be removed in v0.11.0.
Please use --prefer-configured-auth going forward.
[env var: GALACTORY_PREFER_CONFIGURED_KEY]
--publish-skip-configured-key
If set, publish endpoint will not use a configured key, only Galaxy token.
--prefer-configured-auth
If set, prefer the confgured Artifactory auth over the Galaxy token.
[env var: GALACTORY_PREFER_CONFIGURED_AUTH]
--publish-skip-configured-key
If set, publish endpoint will not use configured auth, only auth included in a Galaxy
request.
DEPRECATED: This option will be removed in v0.11.0.
Please use --publish-skip-configured-auth going forward.
[env var: GALACTORY_PUBLISH_SKIP_CONFIGURED_KEY]
--publish-skip-configured-auth
If set, publish endpoint will not use configured auth, only auth included in a Galaxy
request.
[env var: GALACTORY_PUBLISH_SKIP_CONFIGURED_AUTH]
--log-file LOG_FILE If set, logging will go to this file instead of the console.
[env var: GALACTORY_LOG_FILE]
--log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
Expand All @@ -96,8 +122,8 @@ optional arguments:
Look for upsteam caches and use their values.
[env var: GALACTORY_CACHE_READ]
--cache-write CACHE_WRITE
Populate the upstream cache in Artifactory. Should be false when no API key is
provided or the key has no permission to write.
Populate the upstream cache in Artifactory. Should be false when no auth is
provided or the auth has no permission to write.
[env var: GALACTORY_CACHE_WRITE]
--use-property-fallback
Set properties of an uploaded collection in a separate request after publshinng.
Expand Down
9 changes: 9 additions & 0 deletions changelogs/fragments/77-access-token-bearer-auth-support.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
minor_changes:
- Add support for Artifactory Access Tokens (bearer auth) in both configured auth and galaxy requests, via the new ``ARTIFACTORY_ACCESS_TOKEN`` and ``GALAXY_AUTH_TYPE`` configuration options (https://github.com/briantist/galactory/pull/77).

deprecated_features:
- The default value of the new ``GALAXY_AUTH_TYPE`` configuration option, added in this release, will change from ``api_key`` to ``access_token`` in ``v0.11.0`` (https://github.com/briantist/galactory/pull/77).
- The ``PREFER_CONFIGURED_KEY`` configuration option has been replaced by ``PREFER_CONFIGURED_AUTH`` and the old name will be removed in ``v0.11.0`` (https://github.com/briantist/galactory/pull/77).
- The ``USE_GALAXY_KEY`` configuration option has been replaced by ``USE_GALAXY_AUTH`` and the old name will be removed in ``v0.11.0`` (https://github.com/briantist/galactory/pull/77).
- The ``PUBLISH_SKIP_CONFIGURED_KEY`` configuration option has been replaced by ``PUBLISH_SKIP_CONFIGURED_AUTH`` and the old name will be removed in ``v0.11.0`` (https://github.com/briantist/galactory/pull/77).
73 changes: 65 additions & 8 deletions galactory/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# (c) 2022 Brian Scholer (@briantist)

import logging
import warnings

from flask import Flask, request
from werkzeug.middleware.proxy_fix import ProxyFix
Expand Down Expand Up @@ -72,10 +73,15 @@ def create_configured_app(run=False, parse_known_only=True, parse_allow_abbrev=F
parser.add_argument('--server-name', type=str, env_var='GALACTORY_SERVER_NAME', help='The host name and port of the server, as seen from clients. Used for generating links.')
parser.add_argument('--preferred-url-scheme', type=str, env_var='GALACTORY_PREFERRED_URL_SCHEME', help='Sets the preferred scheme to use when constructing URLs. Defaults to the request scheme, but is unaware of reverse proxies.')
parser.add_argument('--artifactory-path', type=str, required=True, env_var='GALACTORY_ARTIFACTORY_PATH', help='The URL of the path in Artifactory where collections are stored.')
parser.add_argument('--artifactory-api-key', type=str, env_var='GALACTORY_ARTIFACTORY_API_KEY', help='If set, is the API key used to access Artifactory.')
parser.add_argument('--use-galaxy-key', action='store_true', env_var='GALACTORY_USE_GALAXY_KEY', help='If set, uses the Galaxy token as the Artifactory API key.')
parser.add_argument('--prefer-configured-key', action='store_true', env_var='GALACTORY_PREFER_CONFIGURED_KEY', help='If set, prefer the confgured Artifactory key over the Galaxy token.')
parser.add_argument('--publish-skip-configured-key', action='store_true', env_var='GALACTORY_PUBLISH_SKIP_CONFIGURED_KEY', help='If set, publish endpoint will not use a configured key, only Galaxy token.')
parser.add_argument('--artifactory-api-key', type=str, env_var='GALACTORY_ARTIFACTORY_API_KEY', help='If set, is the API key used to access Artifactory. If set with artifactory-access-token, this value will not be used.')
parser.add_argument('--artifactory-access-token', type=str, env_var='GALACTORY_ARTIFACTORY_ACCESS_TOKEN', help='If set, is the Access Token used to access Artifactory. If set with artifactory-api-key, this value will be used and the API key will be ignored.')
parser.add_argument('--use-galaxy-key', action='store_true', env_var='GALACTORY_USE_GALAXY_KEY', help='If set, uses the Galaxy token sent in the request as the Artifactory auth. DEPRECATED: This option will be removed in v0.11.0. Please use --use-galaxy-auth going forward.')
parser.add_argument('--use-galaxy-auth', action='store_true', env_var='GALACTORY_USE_GALAXY_AUTH', help='If set, uses the Galaxy token sent in the request as the Artifactory auth.')
parser.add_argument('--galaxy-auth-type', type=str, env_var='GALACTORY_GALAXY_AUTH_TYPE', choices=['api_key', 'access_token'], help='Auth received via a Galaxy request should be interpreted as this type of auth.')
parser.add_argument('--prefer-configured-key', action='store_true', env_var='GALACTORY_PREFER_CONFIGURED_KEY', help='If set, prefer the confgured Artifactory auth over the Galaxy token. DEPRECATED: This option will be removed in v0.11.0. Please use --prefer-configured-auth going forward.')
parser.add_argument('--prefer-configured-auth', action='store_true', env_var='GALACTORY_PREFER_CONFIGURED_AUTH', help='If set, prefer the confgured Artifactory auth over the Galaxy token.')
parser.add_argument('--publish-skip-configured-key', action='store_true', env_var='GALACTORY_PUBLISH_SKIP_CONFIGURED_KEY', help='If set, publish endpoint will not use configured auth, only auth included in a Galaxy request. DEPRECATED: This option will be removed in v0.11.0. Please use --publish-skip-configured-auth going forward.')
parser.add_argument('--publish-skip-configured-auth', action='store_true', env_var='GALACTORY_PUBLISH_SKIP_CONFIGURED_AUTH', help='If set, publish endpoint will not use configured auth, only auth included in a Galaxy request.')
parser.add_argument('--log-file', type=str, env_var='GALACTORY_LOG_FILE', help='If set, logging will go to this file instead of the console.')
parser.add_argument(
'--log-level',
Expand All @@ -90,7 +96,7 @@ def create_configured_app(run=False, parse_known_only=True, parse_allow_abbrev=F
parser.add_argument('-npns', '--no-proxy-namespace', action='append', default=[], env_var='GALACTORY_NO_PROXY_NAMESPACE', help='Requests for this namespace should never be proxied. Can be specified multiple times.')
parser.add_argument('--cache-minutes', default=60, type=int, env_var='GALACTORY_CACHE_MINUTES', help='The time period that a cache entry should be considered valid.')
parser.add_argument('--cache-read', action=_StrBool, default=True, env_var='GALACTORY_CACHE_READ', help='Look for upsteam caches and use their values.')
parser.add_argument('--cache-write', action=_StrBool, default=True, env_var='GALACTORY_CACHE_WRITE', help='Populate the upstream cache in Artifactory. Should be false when no API key is provided or the key has no permission to write.')
parser.add_argument('--cache-write', action=_StrBool, default=True, env_var='GALACTORY_CACHE_WRITE', help='Populate the upstream cache in Artifactory. Should be false when no auth is provided or the auth has no permission to write.')
parser.add_argument('--use-property-fallback', action='store_true', env_var='GALACTORY_USE_PROPERTY_FALLBACK', help='Set properties of an uploaded collection in a separate request after publshinng. Requires a Pro license of Artifactory. This feature is a workaround for an Artifactory proxy configuration error and may be removed in a future version.')
parser.add_argument('--health-check-custom-text', type=str, default='', env_var='GALACTORY_HEALTH_CHECK_CUSTOM_TEXT', help='Sets custom_text field for health check endpoint responses.')

Expand All @@ -101,16 +107,67 @@ def create_configured_app(run=False, parse_known_only=True, parse_allow_abbrev=F

logging.basicConfig(filename=args.log_file, level=args.log_level)

# TODO: v0.11.0 - remove conditional old name
if args.use_galaxy_key and not args.use_galaxy_auth:
use_galaxy_auth = True
warnings.warn(
message=(
"USE_GALAXY_KEY has been replaced by USE_GALAXY_AUTH and the old name will be removed in v0.11.0."
" To suppress this warning, set USE_GALAXY_AUTH."
), category=DeprecationWarning, stacklevel=2
)
else:
use_galaxy_auth = args.use_galaxy_auth

# TODO: v0.11.0 - remove conditional & warning, set default on argument
if args.galaxy_auth_type is None and use_galaxy_auth:
galaxy_auth_type = 'api_key'
warnings.warn(
message=(
"USE_GALAXY_AUTH is True but GALAXY_AUTH_TYPE is not set."
" The default value used will be 'api_key' for backward compatibility, but will change to 'access_token' in v0.11.0."
" To suppress this warning, set an explicit value."
), category=FutureWarning, stacklevel=2
)
else:
galaxy_auth_type = args.galaxy_auth_type

# TODO: v0.11.0 - remove conditional old name
if args.prefer_configured_key and not args.prefer_configured_auth:
prefer_configured_auth = True
warnings.warn(
message=(
"PREFER_CONFIGURED_KEY has been replaced by PREFER_CONFIGURED_AUTH and the old name will be removed in v0.11.0."
" To suppress this warning, set PREFER_CONFIGURED_AUTH."
), category=DeprecationWarning, stacklevel=2
)
else:
prefer_configured_auth = args.prefer_configured_auth

# TODO: v0.11.0 - remove conditional old name
if args.publish_skip_configured_key and not args.publish_skip_configured_auth:
publish_skip_configured_auth = True
warnings.warn(
message=(
"PUBLISH_SKIP_CONFIGURED_KEY has been replaced by PUBLISH_SKIP_CONFIGURED_AUTH and the old name will be removed in v0.11.0."
" To suppress this warning, set PUBLISH_SKIP_CONFIGURED_AUTH."
), category=DeprecationWarning, stacklevel=2
)
else:
publish_skip_configured_auth = args.publish_skip_configured_auth

app = create_app(
ARTIFACTORY_PATH=ArtifactoryPath(args.artifactory_path),
LOG_HEADERS=args.log_headers,
LOG_BODY=args.log_body,
PROXY_UPSTREAM=args.proxy_upstream,
NO_PROXY_NAMESPACES=args.no_proxy_namespace,
ARTIFACTORY_API_KEY=args.artifactory_api_key,
USE_GALAXY_KEY=args.use_galaxy_key,
PREFER_CONFIGURED_KEY=args.prefer_configured_key,
PUBLISH_SKIP_CONFIGURED_KEY=args.publish_skip_configured_key,
ARTIFACTORY_ACCESS_TOKEN=args.artifactory_access_token,
USE_GALAXY_AUTH=use_galaxy_auth,
GALAXY_AUTH_TYPE=galaxy_auth_type,
PREFER_CONFIGURED_AUTH=prefer_configured_auth,
PUBLISH_SKIP_CONFIGURED_AUTH=publish_skip_configured_auth,
SERVER_NAME=args.server_name,
PREFERRED_URL_SCHEME=args.preferred_url_scheme,
CACHE_MINUTES=args.cache_minutes,
Expand Down
4 changes: 2 additions & 2 deletions galactory/api/v2/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,11 @@ def version(namespace, collection, version):
def publish():
sha256 = request.form['sha256']
file = request.files['file']
skip_configured_key = current_app.config['PUBLISH_SKIP_CONFIGURED_KEY']
skip_configured_auth = current_app.config['PUBLISH_SKIP_CONFIGURED_AUTH']
property_fallback = current_app.config.get('USE_PROPERTY_FALLBACK', False)
_scheme = current_app.config.get('PREFERRED_URL_SCHEME')

target = authorize(request, current_app.config['ARTIFACTORY_PATH'] / file.filename, skip_configured_key=skip_configured_key)
target = authorize(request, current_app.config['ARTIFACTORY_PATH'] / file.filename, skip_configured_auth=skip_configured_auth)

with _chunk_to_temp(Base64IO(file)) as tmp:
if tmp.sha256 != sha256:
Expand Down
26 changes: 17 additions & 9 deletions galactory/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from flask import url_for, request, current_app, abort, Response, Request
from flask.json.provider import DefaultJSONProvider
from artifactory import ArtifactoryPath, ArtifactoryException
from dohq_artifactory.auth import XJFrogArtApiAuth
from dohq_artifactory.auth import XJFrogArtApiAuth, XJFrogArtBearerAuth

from . import constants as C
from .iter_tar import iter_tar
Expand Down Expand Up @@ -48,19 +48,27 @@ def _session_with_retries(retry=None, auth=None) -> Session:
return session


def authorize(request: Request, artifactory_path: ArtifactoryPath, retry=None, skip_configured_key: bool = False) -> ArtifactoryPath:
def authorize(request: Request, artifactory_path: ArtifactoryPath, retry=None, skip_configured_auth: bool = False) -> ArtifactoryPath:
auth = None
apikey = None
if not skip_configured_key:
if not skip_configured_auth:
accesstoken = current_app.config['ARTIFACTORY_ACCESS_TOKEN']
apikey = current_app.config['ARTIFACTORY_API_KEY']
if accesstoken is not None:
auth = XJFrogArtBearerAuth(accesstoken)
elif apikey is not None:
auth = XJFrogArtApiAuth(apikey)

if current_app.config['USE_GALAXY_KEY'] and (not current_app.config['PREFER_CONFIGURED_KEY'] or not apikey):
if current_app.config['USE_GALAXY_AUTH'] and (not current_app.config['PREFER_CONFIGURED_AUTH'] or auth is None):
galaxy_auth_type = current_app.config['GALAXY_AUTH_TYPE']
authorization = request.headers.get('Authorization')
if authorization:
apikey = authorization.split(' ')[1]

if apikey:
auth = XJFrogArtApiAuth(apikey)
token = authorization.split(' ')[1]
if galaxy_auth_type == 'access_token':
auth = XJFrogArtBearerAuth(token)
elif galaxy_auth_type == 'api_key':
auth = XJFrogArtApiAuth(token)
else:
raise ValueError(f"Unknown galaxy auth type '{galaxy_auth_type}'.")

session = _session_with_retries(retry=retry, auth=auth)
return ArtifactoryPath(artifactory_path, session=session)
Expand Down

0 comments on commit e458e5d

Please sign in to comment.