ktconvertor 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ktconvertor-0.1.0/LICENSE +21 -0
- ktconvertor-0.1.0/PKG-INFO +32 -0
- ktconvertor-0.1.0/README.md +10 -0
- ktconvertor-0.1.0/pyproject.toml +41 -0
- ktconvertor-0.1.0/setup.cfg +4 -0
- ktconvertor-0.1.0/src/ktconvertor/__init__.py +0 -0
- ktconvertor-0.1.0/src/ktconvertor/convertor.py +118 -0
- ktconvertor-0.1.0/src/ktconvertor.egg-info/PKG-INFO +32 -0
- ktconvertor-0.1.0/src/ktconvertor.egg-info/SOURCES.txt +14 -0
- ktconvertor-0.1.0/src/ktconvertor.egg-info/dependency_links.txt +1 -0
- ktconvertor-0.1.0/src/ktconvertor.egg-info/entry_points.txt +2 -0
- ktconvertor-0.1.0/src/ktconvertor.egg-info/requires.txt +11 -0
- ktconvertor-0.1.0/src/ktconvertor.egg-info/top_level.txt +2 -0
- ktconvertor-0.1.0/src/main.py +53 -0
- ktconvertor-0.1.0/tests/test_convertor.py +16 -0
- ktconvertor-0.1.0/tests/test_genCachePath.py +55 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 pengfei liu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ktconvertor
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: This tool can convert kerberos ticket of format .kirbi to standard MIT CCACHE format
|
|
5
|
+
Author-email: Pengfei Liu <pengfei.liu@casd.eu>
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: kerberos,ccache
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: pyasn1
|
|
12
|
+
Requires-Dist: minikerberos
|
|
13
|
+
Requires-Dist: asn1crypto
|
|
14
|
+
Requires-Dist: typer
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
17
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
18
|
+
Requires-Dist: black>=23.0; extra == "dev"
|
|
19
|
+
Requires-Dist: isort>=5.12; extra == "dev"
|
|
20
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# KtConvertor
|
|
24
|
+
|
|
25
|
+
The objective of this project is to convert a kerberos ticket from format `.kirbi` to MIT Kerberos cache file.
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
We use the `ccache.py` of the project [minikerberos](https://github.com/skelsec/minikerberos/blob/main/minikerberos/common/ccache.py)
|
|
29
|
+
|
|
30
|
+
##
|
|
31
|
+
|
|
32
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# KtConvertor
|
|
2
|
+
|
|
3
|
+
The objective of this project is to convert a kerberos ticket from format `.kirbi` to MIT Kerberos cache file.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
We use the `ccache.py` of the project [minikerberos](https://github.com/skelsec/minikerberos/blob/main/minikerberos/common/ccache.py)
|
|
7
|
+
|
|
8
|
+
##
|
|
9
|
+
|
|
10
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ktconvertor"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "This tool can convert kerberos ticket of format .kirbi to standard MIT CCACHE format"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Pengfei Liu", email = "pengfei.liu@casd.eu" }
|
|
10
|
+
]
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
|
|
13
|
+
keywords = ["kerberos", "ccache", ]
|
|
14
|
+
|
|
15
|
+
dependencies = ["pyasn1", "minikerberos","asn1crypto", "typer"
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.optional-dependencies]
|
|
19
|
+
# Development dependencies (install with: pip install -e ".[dev]")
|
|
20
|
+
dev = [
|
|
21
|
+
"pytest>=7.0",
|
|
22
|
+
"pytest-cov>=4.0",
|
|
23
|
+
"black>=23.0", # Code formatter
|
|
24
|
+
"isort>=5.12", # Import sorter
|
|
25
|
+
"mypy>=1.0", # Added for type checking
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.scripts]
|
|
29
|
+
# This allows the user to run 'convert-tgt' directly in the terminal
|
|
30
|
+
convert-tgt = "ktconvertor.main:app"
|
|
31
|
+
|
|
32
|
+
[build-system]
|
|
33
|
+
requires = ["setuptools>=61.0"]
|
|
34
|
+
build-backend = "setuptools.build_meta"
|
|
35
|
+
|
|
36
|
+
# Modern project tip: Add configuration for your tools here
|
|
37
|
+
[tool.black]
|
|
38
|
+
line-length = 88
|
|
39
|
+
|
|
40
|
+
[tool.isort]
|
|
41
|
+
profile = "black"
|
|
File without changes
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import getpass
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from asn1crypto import core
|
|
9
|
+
from minikerberos.common.ccache import CCACHE, Credential, CCACHEPrincipal, Times, Keyblock, CCACHEOctetString
|
|
10
|
+
from minikerberos.common.kirbi import Kirbi
|
|
11
|
+
from minikerberos.protocol.asn1_structs import EncKrbCredPart, TicketFlags, Ticket
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def from_kirbi(kirbi: Kirbi):
|
|
15
|
+
krbcred = kirbi.kirbiobj.native
|
|
16
|
+
c = Credential()
|
|
17
|
+
enc_credinfo = EncKrbCredPart.load(krbcred['enc-part']['cipher']).native
|
|
18
|
+
ticket_info = enc_credinfo['ticket-info'][0]
|
|
19
|
+
|
|
20
|
+
c.client = CCACHEPrincipal.from_asn1(ticket_info['pname'], ticket_info['prealm'])
|
|
21
|
+
# yaaaaay 4 additional weirdness!!!!
|
|
22
|
+
# if sname name-string contains a realm as well htne impacket will crash miserably :(
|
|
23
|
+
if len(ticket_info['sname']['name-string']) > 2 and ticket_info['sname']['name-string'][-1].upper() == ticket_info[
|
|
24
|
+
'srealm'].upper():
|
|
25
|
+
print('SNAME contains the realm as well, trimming it')
|
|
26
|
+
t = ticket_info['sname']
|
|
27
|
+
t['name-string'] = t['name-string'][:-1]
|
|
28
|
+
c.server = CCACHEPrincipal.from_asn1(t, ticket_info['srealm'])
|
|
29
|
+
else:
|
|
30
|
+
c.server = CCACHEPrincipal.from_asn1(ticket_info['sname'], ticket_info['srealm'])
|
|
31
|
+
|
|
32
|
+
c.time = Times.from_asn1(ticket_info)
|
|
33
|
+
c.key = Keyblock.from_asn1(ticket_info['key'])
|
|
34
|
+
c.is_skey = 0 # not sure!
|
|
35
|
+
|
|
36
|
+
c.tktflags = TicketFlags(ticket_info['flags']).cast(core.IntegerBitString).native
|
|
37
|
+
c.num_address = 0
|
|
38
|
+
c.num_authdata = 0
|
|
39
|
+
c.ticket = CCACHEOctetString.from_asn1(
|
|
40
|
+
Ticket(krbcred['tickets'][0]).dump()) # kirbi only stores one ticket per file
|
|
41
|
+
c.second_ticket = CCACHEOctetString.empty()
|
|
42
|
+
|
|
43
|
+
return c
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def gen_cache_path(user: Optional[str] = None) -> str:
|
|
47
|
+
"""
|
|
48
|
+
Generate MIT Kerberos ccache file path following OS-specific standards.
|
|
49
|
+
|
|
50
|
+
Refined to prioritize XDG specs on Linux and robust path handling on Windows.
|
|
51
|
+
"""
|
|
52
|
+
if sys.platform == "darwin":
|
|
53
|
+
# macOS typically uses API-based credential caches (KCM), not flat files.
|
|
54
|
+
raise NotImplementedError("macOS uses CCAPI; file-based paths are non-standard.")
|
|
55
|
+
|
|
56
|
+
if os.name == "nt":
|
|
57
|
+
# Windows best practice: Use USERPROFILE or LOCALAPPDATA for caches
|
|
58
|
+
user = user or getpass.getuser()
|
|
59
|
+
base = Path(os.environ.get("USERPROFILE", f"C:/Users/{user}"))
|
|
60
|
+
return (base / f"krb5cc_{user}").as_posix()
|
|
61
|
+
|
|
62
|
+
# Linux / Unix / POSIX
|
|
63
|
+
# 1. Check for XDG_RUNTIME_DIR (Modern Linux standard, e.g., /run/user/1000)
|
|
64
|
+
# 2. Fallback to /tmp with UID
|
|
65
|
+
uid = os.getuid()
|
|
66
|
+
runtime_dir = os.environ.get("XDG_RUNTIME_DIR")
|
|
67
|
+
|
|
68
|
+
if runtime_dir:
|
|
69
|
+
base_path = Path(runtime_dir)
|
|
70
|
+
else:
|
|
71
|
+
base_path = Path("/tmp")
|
|
72
|
+
|
|
73
|
+
return (base_path / f"krb5cc_{uid}").as_posix()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def convert_kirbi(src:str, dest:str=None)->str:
|
|
77
|
+
"""
|
|
78
|
+
This function convert the kirbi format to MIT ccache format.
|
|
79
|
+
:param src: The path of source .kirbi file which contains the tgt binary in base64 format
|
|
80
|
+
:param dest: The path of converted MIT ccache file
|
|
81
|
+
:return:
|
|
82
|
+
"""
|
|
83
|
+
# 1. Standardize path handling using Pathlib
|
|
84
|
+
# This handles ../, ./, absolute paths, and OS-specific separators ( \ vs / )
|
|
85
|
+
src_path = Path(src).expanduser().resolve()
|
|
86
|
+
|
|
87
|
+
if not src_path.exists():
|
|
88
|
+
raise FileNotFoundError(f"Source kirbi file not found: {src_path}")
|
|
89
|
+
|
|
90
|
+
# 2. Use context managers for safe File I/O
|
|
91
|
+
# This ensures the file handle is closed even if decoding fails
|
|
92
|
+
try:
|
|
93
|
+
with src_path.open("rb") as f:
|
|
94
|
+
kirbi_b64 = f.read()
|
|
95
|
+
|
|
96
|
+
# 3. Decode Base64 to raw bytes
|
|
97
|
+
kirbi_bytes = base64.b64decode(kirbi_b64)
|
|
98
|
+
except Exception as e:
|
|
99
|
+
raise ValueError(f"Failed to read or decode kirbi file: {e}")
|
|
100
|
+
|
|
101
|
+
# 4. Process the credential cache
|
|
102
|
+
cc = CCACHE.from_bytes(kirbi_bytes)
|
|
103
|
+
|
|
104
|
+
# 5. Determine destination
|
|
105
|
+
if dest:
|
|
106
|
+
dest_path = Path(dest).expanduser().resolve()
|
|
107
|
+
else:
|
|
108
|
+
# Fallback to our logic from the previous gen_cache_path function
|
|
109
|
+
dest_path = Path(gen_cache_path())
|
|
110
|
+
|
|
111
|
+
# Ensure the parent directory for the destination exists
|
|
112
|
+
dest_path.parent.mkdir(parents=True, exist_ok=True)
|
|
113
|
+
|
|
114
|
+
# 6. Write ticket to the file path
|
|
115
|
+
final_path = dest_path.as_posix()
|
|
116
|
+
cc.to_file(final_path)
|
|
117
|
+
|
|
118
|
+
return final_path
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ktconvertor
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: This tool can convert kerberos ticket of format .kirbi to standard MIT CCACHE format
|
|
5
|
+
Author-email: Pengfei Liu <pengfei.liu@casd.eu>
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: kerberos,ccache
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: pyasn1
|
|
12
|
+
Requires-Dist: minikerberos
|
|
13
|
+
Requires-Dist: asn1crypto
|
|
14
|
+
Requires-Dist: typer
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
17
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
18
|
+
Requires-Dist: black>=23.0; extra == "dev"
|
|
19
|
+
Requires-Dist: isort>=5.12; extra == "dev"
|
|
20
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# KtConvertor
|
|
24
|
+
|
|
25
|
+
The objective of this project is to convert a kerberos ticket from format `.kirbi` to MIT Kerberos cache file.
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
We use the `ccache.py` of the project [minikerberos](https://github.com/skelsec/minikerberos/blob/main/minikerberos/common/ccache.py)
|
|
29
|
+
|
|
30
|
+
##
|
|
31
|
+
|
|
32
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/main.py
|
|
5
|
+
src/ktconvertor/__init__.py
|
|
6
|
+
src/ktconvertor/convertor.py
|
|
7
|
+
src/ktconvertor.egg-info/PKG-INFO
|
|
8
|
+
src/ktconvertor.egg-info/SOURCES.txt
|
|
9
|
+
src/ktconvertor.egg-info/dependency_links.txt
|
|
10
|
+
src/ktconvertor.egg-info/entry_points.txt
|
|
11
|
+
src/ktconvertor.egg-info/requires.txt
|
|
12
|
+
src/ktconvertor.egg-info/top_level.txt
|
|
13
|
+
tests/test_convertor.py
|
|
14
|
+
tests/test_genCachePath.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
|
|
6
|
+
# Adjust the import to match your package structure
|
|
7
|
+
from ktconvertor.convertor import convert_kirbi
|
|
8
|
+
|
|
9
|
+
# Initialize console for professional-looking output
|
|
10
|
+
console = Console()
|
|
11
|
+
|
|
12
|
+
app = typer.Typer(
|
|
13
|
+
name="krbconvertor",
|
|
14
|
+
help="CLI tool to convert Kerberos .kirbi tickets to MIT CCACHE format.",
|
|
15
|
+
rich_markup_mode="rich"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@app.command()
|
|
20
|
+
def convert(
|
|
21
|
+
kirbi_path: Path = typer.Argument(
|
|
22
|
+
...,
|
|
23
|
+
exists=True,
|
|
24
|
+
file_okay=True,
|
|
25
|
+
dir_okay=False,
|
|
26
|
+
readable=True,
|
|
27
|
+
resolve_path=True,
|
|
28
|
+
help="Path to the input .kirbi ticket file."
|
|
29
|
+
),
|
|
30
|
+
ccache_path: Optional[Path] = typer.Option(
|
|
31
|
+
None,
|
|
32
|
+
"--output", "-o",
|
|
33
|
+
writable=True,
|
|
34
|
+
help="Path for the output .ccache file. If omitted, uses the default OS cache path."
|
|
35
|
+
),
|
|
36
|
+
):
|
|
37
|
+
"""
|
|
38
|
+
Convert a Kerberos .kirbi ticket to .ccache format.
|
|
39
|
+
"""
|
|
40
|
+
try:
|
|
41
|
+
# Convert Path object back to string if your core function requires it,
|
|
42
|
+
# though it's better if convert_kirbi handles Path objects.
|
|
43
|
+
final_path = convert_kirbi(str(kirbi_path), str(ccache_path) if ccache_path else None)
|
|
44
|
+
|
|
45
|
+
console.print(f"[bold green]✓ Success:[/bold green] Ticket converted to [cyan]{final_path}[/cyan]")
|
|
46
|
+
|
|
47
|
+
except Exception as e:
|
|
48
|
+
console.print(f"[bold red]✗ Error:[/bold red] {e}")
|
|
49
|
+
raise typer.Exit(code=1)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
app()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from ktconvertor.convertor import convert_kirbi
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_convertor_with_given_path():
|
|
5
|
+
kirbi = "C:/Users/pliu/Documents/git/krbTicketConvertor/tests/tmp/tgt.kirbi"
|
|
6
|
+
ccache = "C:/Users/pliu/Documents/git/krbTicketConvertor/tests/tmp/tgt.ccache"
|
|
7
|
+
|
|
8
|
+
convert_kirbi(kirbi, ccache)
|
|
9
|
+
|
|
10
|
+
def test_convertor_with_default_path():
|
|
11
|
+
kirbi = "C:/Users/pliu/Documents/git/krbTicketConvertor/tests/tmp/tgt.kirbi"
|
|
12
|
+
convert_kirbi(kirbi)
|
|
13
|
+
|
|
14
|
+
def test_convertor_with_relative_path():
|
|
15
|
+
kirbi = "./tests/tmp/tgt.kirbi"
|
|
16
|
+
convert_kirbi(kirbi)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import pytest
|
|
4
|
+
from unittest.mock import patch, MagicMock
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from ktconvertor.convertor import gen_cache_path
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestGenCachePath:
|
|
12
|
+
|
|
13
|
+
@patch("sys.platform", "darwin")
|
|
14
|
+
def test_macos_raises_error(self):
|
|
15
|
+
"""Ensure macOS triggers the expected NotImplementedError."""
|
|
16
|
+
with pytest.raises(NotImplementedError, match="macOS uses CCAPI"):
|
|
17
|
+
gen_cache_path()
|
|
18
|
+
|
|
19
|
+
@patch("os.name", "nt")
|
|
20
|
+
@patch("getpass.getuser", return_value="alice")
|
|
21
|
+
@patch.dict(os.environ, {"USERPROFILE": "C:\\Users\\alice"})
|
|
22
|
+
def test_windows_path_generation(self, mock_getuser):
|
|
23
|
+
"""Verify Windows path logic using environment variables."""
|
|
24
|
+
path = gen_cache_path()
|
|
25
|
+
# Pathlib handles the slash conversion, .as_posix() ensures forward slashes
|
|
26
|
+
assert path == "C:/Users/alice/krb5cc_alice"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@patch("os.name", "nt")
|
|
30
|
+
@patch.dict(os.environ, {"USERPROFILE": "C:\\Users\\bob"})
|
|
31
|
+
def test_explicit_user_override(self):
|
|
32
|
+
"""Ensure providing a 'user' argument overrides the system user."""
|
|
33
|
+
path = gen_cache_path(user="admin")
|
|
34
|
+
assert path == "C:/Users/bob/krb5cc_admin"
|
|
35
|
+
|
|
36
|
+
####### The linux os test will fail if you run under windows ###############
|
|
37
|
+
# the os module is dynamic; attributes like getuid or setuid simply do not exist when Python runs on Windows.
|
|
38
|
+
@patch("os.name", "posix")
|
|
39
|
+
@patch("sys.platform", "linux")
|
|
40
|
+
@patch("os.getuid", return_value=1000)
|
|
41
|
+
@patch.dict(os.environ, {}, clear=True)
|
|
42
|
+
def test_linux_fallback_path(self, mock_uid):
|
|
43
|
+
"""Verify fallback to /tmp when XDG_RUNTIME_DIR is missing.
|
|
44
|
+
"""
|
|
45
|
+
path = gen_cache_path()
|
|
46
|
+
assert path == "/tmp/krb5cc_1000"
|
|
47
|
+
|
|
48
|
+
@patch("os.name", "posix")
|
|
49
|
+
@patch("sys.platform", "linux")
|
|
50
|
+
@patch("os.getuid", return_value=1000)
|
|
51
|
+
@patch.dict(os.environ, {"XDG_RUNTIME_DIR": "/run/user/1000"})
|
|
52
|
+
def test_linux_xdg_path(self, mock_uid):
|
|
53
|
+
"""Verify preference for XDG_RUNTIME_DIR on modern Linux systems."""
|
|
54
|
+
path = gen_cache_path()
|
|
55
|
+
assert path == "/run/user/1000/krb5cc_1000"
|