Skip to content

Commit f6fe7db

Browse files
author
László Vaskó
committed
Merge remote-tracking branch 'origin/develop'
2 parents cc87d69 + 8a9d9af commit f6fe7db

File tree

8 files changed

+108
-16
lines changed

8 files changed

+108
-16
lines changed

README.md

+30-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,31 @@
11
# openconnect-sso
2-
Wrapper script for openconnect supporting Azure AD (SAMLv2) authentication
2+
Wrapper script for OpenConnect supporting Azure AD (SAMLv2) authentication
3+
4+
## TL; DR
5+
```bash
6+
$ pip install openconnect-sso-$VERSION.whl
7+
$ openconnect-sso --server vpn.server.com/group
8+
```
9+
10+
## Configuration
11+
If you want to save credentials and get them automatically
12+
injected in the web browser:
13+
```bash
14+
$ openconnect-sso --server vpn.server.com/group --user [email protected]
15+
Password ([email protected]):
16+
[info ] Authenticating to VPN endpoint ...
17+
```
18+
19+
User credentials are automatically saved to the users login keyring (if available).
20+
21+
If you already have Cisco Anyconnect set-up, then `--server` argument is optional.
22+
Also, the last used `--server` address is saved between sessions so there is no need
23+
to always type in the same arguments:
24+
25+
```bash
26+
$ openconnect-sso
27+
[info ] Authenticating to VPN endpoint ...
28+
```
29+
30+
Configuration is saved in `$XDG_CONFIG_HOME/openconnect-sso/config.toml`. On typical
31+
Linux installations it is located under `$HOME/.configopenconnect-sso/config.toml`

openconnect_sso/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "0.1.0"
1+
__version__ = "0.2.0"
22
__description__ = (
3-
"Wrapper script for openconnect supporting Azure AD (SAMLv2) authentication"
3+
"Wrapper script for OpenConnect supporting Azure AD (SAMLv2) authentication"
44
)

openconnect_sso/app.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ async def _run(args):
6363

6464
if cfg.default_profile and not args.use_profile_selector:
6565
selected_profile = cfg.default_profile
66-
else:
66+
elif args.use_profile_selector:
6767
profiles = get_profiles(Path(args.profile_path))
6868
if not profiles:
6969
logger.error("No profile found")
@@ -73,7 +73,14 @@ async def _run(args):
7373
if not selected_profile:
7474
logger.error("No profile selected")
7575
return 18
76-
cfg.default_profile = selected_profile
76+
elif args.server:
77+
selected_profile = config.HostProfile(args.server, args.usergroup)
78+
else:
79+
raise ValueError(
80+
"Cannot determine server address. Invalid arguments specified."
81+
)
82+
83+
cfg.default_profile = selected_profile
7784

7885
config.save(cfg)
7986

openconnect_sso/cli.py

+38-5
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
11
import argparse
22
import enum
33
import logging
4+
import os
45

56
import openconnect_sso
6-
from openconnect_sso import app
7+
from openconnect_sso import app, config
78

89

910
def create_argparser():
1011
parser = argparse.ArgumentParser(
1112
prog="openconnect-sso", description=openconnect_sso.__description__
1213
)
13-
parser.add_argument(
14+
15+
server_settings = parser.add_argument_group("Server connection")
16+
server_settings.add_argument(
1417
"-p",
1518
"--profile",
1619
dest="profile_path",
1720
help="Use a profile from this file or directory",
18-
default="/opt/cisco/anyconnect/profile",
1921
)
2022

21-
parser.add_argument(
23+
server_settings.add_argument(
2224
"-P",
2325
"--profile-selector",
2426
dest="use_profile_selector",
@@ -27,6 +29,20 @@ def create_argparser():
2729
default=False,
2830
)
2931

32+
server_settings.add_argument(
33+
"-s",
34+
"--server",
35+
help="VPN server to connect to. The following forms are accepted: "
36+
"vpn.server.com, vpn.server.com/usergroup, "
37+
"https://vpn.server.com, https.vpn.server.com.usergroup",
38+
)
39+
server_settings.add_argument(
40+
"-g",
41+
"--usergroup",
42+
help="Override usergroup setting from --server argument",
43+
default="",
44+
)
45+
3046
parser.add_argument(
3147
"--login-only",
3248
help="Complete authentication but do not acquire a session token or initiate a connection",
@@ -57,8 +73,9 @@ def create_argparser():
5773

5874

5975
class LogLevel(enum.IntEnum):
60-
INFO = logging.INFO
76+
ERROR = logging.ERROR
6177
WARNING = logging.WARNING
78+
INFO = logging.INFO
6279
DEBUG = logging.DEBUG
6380

6481
def __str__(self):
@@ -76,4 +93,20 @@ def choices(cls):
7693
def main():
7794
parser = create_argparser()
7895
args = parser.parse_args()
96+
97+
if (args.profile_path or args.use_profile_selector) and (
98+
args.server or args.usergroup
99+
):
100+
parser.error(
101+
"--profile/--profile-selector and --server/--usergroup are mutually exclusive"
102+
)
103+
104+
if not args.profile_path and not args.server and not config.load().default_profile:
105+
if os.path.exists("/opt/cisco/anyconnect/profiles"):
106+
args.profile_path = "/opt/cisco/anyconnect/profiles"
107+
else:
108+
parser.error(
109+
"No Anyconnect profile can be found. One of --profile or --server arguments required."
110+
)
111+
79112
return app.run(args)

openconnect_sso/config.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from pathlib import Path
2-
from urllib.parse import urlunparse
2+
from urllib.parse import urlparse, urlunparse
33

44
import attr
55
import keyring
@@ -59,13 +59,19 @@ def as_dict(self):
5959

6060
@attr.s
6161
class HostProfile(ConfigNode):
62-
name = attr.ib(converter=str)
6362
address = attr.ib(converter=str)
6463
user_group = attr.ib(converter=str)
64+
name = attr.ib(converter=str, default="UNNAMED")
6565

6666
@property
6767
def vpn_url(self):
68-
return urlunparse(("https", self.address, self.user_group, "", "", ""))
68+
parts = urlparse(self.address)
69+
group = self.user_group or parts.path
70+
if parts.path == self.address and not self.user_group:
71+
group = ""
72+
return urlunparse(
73+
(parts.scheme or "https", parts.netloc or self.address, group, "", "", "")
74+
)
6975

7076

7177
@attr.s

pyproject.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[tool.poetry]
22
name = "openconnect-sso"
3-
version = "0.1.0"
4-
description = "Wrapper script for openconnect supporting Azure AD (SAMLv2) authentication"
3+
version = "0.2.0"
4+
description = "Wrapper script for OpenConnect supporting Azure AD (SAMLv2) authentication"
55
authors = ["László Vaskó <[email protected]>"]
66
readme = "README.md"
77

tests/test_hostprofile.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import pytest
2+
3+
from openconnect_sso.config import HostProfile
4+
5+
6+
@pytest.mark.parametrize(("server", "group", "expected_url"), (
7+
("hostname", "", "https://hostname"),
8+
("hostname", "group", "https://hostname/group"),
9+
("hostname/group", "", "https://hostname/group"),
10+
("https://hostname", "group", "https://hostname/group"),
11+
("https://server.com", "group", "https://server.com/group"),
12+
("https://hostname/group", "", "https://hostname/group"),
13+
("https://hostname:8443/group", "", "https://hostname:8443/group"),
14+
))
15+
def test_vpn_url(server, group, expected_url):
16+
assert HostProfile(server, group).vpn_url == expected_url
17+

tests/test_openconnect_sso.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33

44
def test_version():
5-
assert __version__ == '0.1.0'
5+
assert __version__ == '0.2.0'

0 commit comments

Comments
 (0)