Skip to content

Commit ed11c91

Browse files
author
zhaoyu
committed
Add refresh function in panda_auth
1 parent b4ee9c7 commit ed11c91

File tree

5 files changed

+97
-4
lines changed

5 files changed

+97
-4
lines changed

doc/changes/DM-48912.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added refresh function in panda_auth

python/lsst/ctrl/bps/panda/cli/cmd/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@
2525
# You should have received a copy of the GNU General Public License
2626
# along with this program. If not, see <https://www.gnu.org/licenses/>.
2727

28-
__all__ = ["clean", "reset", "status"]
28+
__all__ = ["clean", "reset", "refresh", "status"]
2929

30-
from .panda_auth_commands import clean, reset, status
30+
from .panda_auth_commands import clean, reset, refresh, status

python/lsst/ctrl/bps/panda/cli/cmd/panda_auth_commands.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
__all__ = [
3030
"clean",
31+
"refresh",
3132
"reset",
3233
"status",
3334
]
@@ -37,7 +38,12 @@
3738

3839
from lsst.daf.butler.cli.utils import MWCommand
3940

40-
from ...panda_auth_drivers import panda_auth_clean_driver, panda_auth_reset_driver, panda_auth_status_driver
41+
from ...panda_auth_drivers import (
42+
panda_auth_clean_driver,
43+
panda_auth_refresh_driver,
44+
panda_auth_reset_driver,
45+
panda_auth_status_driver,
46+
)
4147

4248

4349
class PandaAuthCommand(MWCommand):
@@ -62,3 +68,13 @@ def reset(*args, **kwargs):
6268
def clean(*args, **kwargs):
6369
"""Clean up token and token cache files."""
6470
panda_auth_clean_driver(*args, **kwargs)
71+
72+
73+
@click.command(cls=PandaAuthCommand)
74+
@click.option("--days", default=4, help="The earlist remaining days to refresh the token.")
75+
@click.option("--verbose", is_flag=True, help="Enable verbose output")
76+
def refresh(*args, **kwargs):
77+
"""Refresh auth tocken."""
78+
days = kwargs.get("days", 4)
79+
verbose = kwargs.get("verbose", False)
80+
panda_auth_refresh_driver(days, verbose)

python/lsst/ctrl/bps/panda/panda_auth_drivers.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,12 @@
4141
import logging
4242
from datetime import datetime
4343

44-
from .panda_auth_utils import panda_auth_clean, panda_auth_status, panda_auth_update
44+
from .panda_auth_utils import (
45+
panda_auth_clean,
46+
panda_auth_refresh,
47+
panda_auth_status,
48+
panda_auth_update,
49+
)
4550

4651
_LOG = logging.getLogger(__name__)
4752

@@ -56,6 +61,11 @@ def panda_auth_reset_driver():
5661
panda_auth_update(None, True)
5762

5863

64+
def panda_auth_refresh_driver(days, verbose):
65+
"""Get new auth token."""
66+
panda_auth_refresh(days, verbose)
67+
68+
5969
def panda_auth_status_driver():
6070
"""Gather information about a token if it exists."""
6171
status = panda_auth_status()

python/lsst/ctrl/bps/panda/panda_auth_utils.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,18 @@
3030
__all__ = [
3131
"panda_auth_clean",
3232
"panda_auth_expiration",
33+
"panda_auth_refresh",
3334
"panda_auth_setup",
3435
"panda_auth_status",
3536
"panda_auth_update",
3637
]
3738

3839

40+
import base64
41+
import json
3942
import logging
4043
import os
44+
from datetime import datetime, timedelta
4145

4246
import idds.common.utils as idds_utils
4347
import pandaclient.idds_api
@@ -151,3 +155,65 @@ def panda_auth_update(idds_server=None, reset=False):
151155
# idds server given. So for now, check result string for keywords.
152156
if "request_id" not in ret[1][-1] or "status" not in ret[1][-1]:
153157
raise RuntimeError(f"Error contacting PanDA service: {ret}")
158+
159+
160+
def panda_auth_refresh(days=4, verbose=False):
161+
"""Refresh auth token"""
162+
panda_url = os.environ.get("PANDA_URL")
163+
panda_auth_vo = os.environ.get("PANDA_AUTH_VO")
164+
url_prefix = panda_url.split("/server", 1)[0]
165+
auth_url = f"{url_prefix}/auth/{panda_auth_vo}_auth_config.json"
166+
open_id = OpenIdConnect_Utils(auth_url, log_stream=_LOG, verbose=verbose)
167+
168+
token_file = open_id.get_token_path()
169+
if os.path.exists(token_file):
170+
with open(token_file) as f:
171+
data = json.load(f)
172+
enc = data["id_token"].split(".")[1]
173+
enc += "=" * (-len(enc) % 4)
174+
dec = json.loads(base64.urlsafe_b64decode(enc.encode()))
175+
exp_time = datetime.utcfromtimestamp(dec["exp"])
176+
delta = exp_time - datetime.utcnow()
177+
minutes = delta.total_seconds() / 60
178+
print(f"Token will expire in {minutes} minutes.")
179+
print(f"Token expiration time : {exp_time.strftime('%Y-%m-%d %H:%M:%S')} UTC")
180+
if delta < timedelta(minutes=0):
181+
print("Token already expired. Cannot refresh.")
182+
return
183+
elif delta > timedelta(days=days):
184+
print("\n" + "=" * 60)
185+
print(f" Too early to refresh. More than {days} day(s) until expiration.")
186+
print(" To change this threshold, use the --days option:")
187+
print(" Example: panda_auth refresh --days 10")
188+
print("=" * 60 + "\n")
189+
return
190+
else:
191+
print("Cannot find token file.")
192+
return
193+
refresh_token_string = data["refresh_token"]
194+
195+
s, auth_config = open_id.fetch_page(open_id.auth_config_url)
196+
if not s:
197+
print("Failed to get Auth configuration")
198+
return
199+
200+
s, endpoint_config = open_id.fetch_page(auth_config["oidc_config_url"])
201+
if not s:
202+
print("Failed to get endpoint configuration")
203+
return
204+
205+
s, o = open_id.refresh_token(
206+
endpoint_config["token_endpoint"],
207+
auth_config["client_id"],
208+
auth_config["client_secret"],
209+
refresh_token_string,
210+
)
211+
212+
if not s:
213+
print("Failed to refresh token")
214+
return
215+
else:
216+
status = panda_auth_status()
217+
if status:
218+
print(f"{'New expiration time:':23} {datetime.utcfromtimestamp(status['exp'])} UTC")
219+
print("Success to refresh token")

0 commit comments

Comments
 (0)