Skip to content
Open
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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- Add basic auth support for Redmine instances

## [0.6.0] - 2024-05-14
### Added
Expand Down Expand Up @@ -103,4 +105,3 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
[0.1.0]: https://github.com/nekeal/issx/compare/0.0.4...0.1.0
[0.0.4]: https://github.com/nekeal/issx/compare/0.0.2...0.0.4
[0.0.2]: https://github.com/nekeal/issx/tree/0.0.2

14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,20 @@ that `issx` will interact with.
an instance.
`project` field should contain the project id available in the chosen instance (usually it is a number).

### Basic auth

For Redmine instances that use basic authentication, you can specify the credentials using `basic_auth_user` and
`basic_auth_password` properties in the instance configuration:

Comment on lines +134 to +136
Copy link
Owner

Choose a reason for hiding this comment

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

I would prefer keeping description of this section generic, and explicitely enumerating supported instances.
This way it will be easy to add support for basic auth to the rest of instances in the future? What do you think?

```toml
[instances.INSTANCE_NAME]
backend = "redmine"
url = "<absolute url to the instance>"
token = "<API token used for authentication>"
basic_auth_user = "username"
basic_auth_password = "password"
```

## Development

* Clone this repository
Expand Down
28 changes: 26 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ python-kacl = "*"
ruff = "*"
pytest-asyncio = "^0.23.6"
import-linter = "^2.0"
types-requests = "^2.32.0.20250328"

[tool.poetry.group.docs.dependencies]
mkdocstrings = {version = ">=0.23", extras = ["python"]}
Expand Down
12 changes: 12 additions & 0 deletions src/issx/clients/redmine.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from redminelib.resources import Issue as RedmineIssue
from redminelib.resources import Project
from redminelib.resultsets import ResourceSet
from requests.auth import HTTPBasicAuth

from issx.clients.exceptions import IssueDoesNotExistError, ProjectDoesNotExistError
from issx.clients.interfaces import InstanceClientInterface, IssueClientInterface
Expand Down Expand Up @@ -46,10 +47,21 @@ def get_instance_url(self) -> str:
@classmethod
def instance_from_config(cls, instance_config: InstanceConfig) -> Self:
instance_config = cls.instance_config_class(**asdict(instance_config))
additional_kwargs = {}
if instance_config.basic_auth_user and instance_config.basic_auth_password:
additional_kwargs = {
"requests": {
"auth": HTTPBasicAuth(
instance_config.basic_auth_user,
instance_config.basic_auth_password,
)
}
}
return cls(
Redmine(
instance_config.url,
key=instance_config.token,
**additional_kwargs,
)
)

Expand Down
8 changes: 8 additions & 0 deletions src/issx/domain/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ class InstanceConfig(BaseConfig):
)
url: str = attr.ib(validator=attr.validators.instance_of(str))
token: str = attr.ib(validator=attr.validators.instance_of(str))
basic_auth_user: str | None = attr.ib(
default=None,
validator=attr.validators.optional(attr.validators.instance_of(str)),
)
basic_auth_password: str | None = attr.ib(
default=None,
validator=attr.validators.optional(attr.validators.instance_of(str)),
)


@define(kw_only=True)
Expand Down
23 changes: 22 additions & 1 deletion tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ def test_meaningful_fields(self):
fields.backend,
fields.url,
fields.token,
fields.basic_auth_user,
fields.basic_auth_password,
]

def test_as_toml(self):
Expand All @@ -21,5 +23,24 @@ def test_as_toml(self):
"[test]\n"
"backend = 'gitlab'\n"
"url = 'http://example.com'\n"
"token = '123'"
"token = '123'\n"
"basic_auth_user = None\n"
"basic_auth_password = None"
)

def test_as_toml_with_basic_auth(self):
config = InstanceConfig(
backend="redmine", # type: ignore
url="http://example.com",
token="123",
basic_auth_user="myuser",
basic_auth_password="mypassword",
)
assert config.as_toml("test") == (
"[test]\n"
"backend = 'redmine'\n"
"url = 'http://example.com'\n"
"token = '123'\n"
"basic_auth_user = 'myuser'\n"
"basic_auth_password = 'mypassword'"
)