diff mbox series

[1/3] fetch2/npm: Add basic auth credential support for npm fetcher

Message ID 20250127142918.163-2-eric.meyers@arthrex.com
State New
Headers show
Series NPM/NPM Shrinkwrap Basic Authentication Credential Support: | expand

Commit Message

Eric Meyers Jan. 27, 2025, 2:29 p.m. UTC
From: Eric Meyers <eric.meyers@arthrex.com>

Signed-off-by: Eric Meyers <eric.meyers@arthrex.com>
Cc: Geoff Parker <geoffrey.parker@arthrex.com>
---
 lib/bb/fetch2/npm.py | 64 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 63 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/lib/bb/fetch2/npm.py b/lib/bb/fetch2/npm.py
index ac76d64cd..4bf59dd1e 100644
--- a/lib/bb/fetch2/npm.py
+++ b/lib/bb/fetch2/npm.py
@@ -21,6 +21,15 @@  Supported SRC_URI options are:
 
 - destsuffix
     Specifies the directory to use to unpack the package (default: npm).
+
+- namespace
+   The npm namespace name (without the preceding "@").
+
+- username
+    Specifies the basic auth username to use when accessing the npm registry.
+
+- password
+    Specifies the basic auth password (or API key) to use when accessing the npm registry.
 """
 
 import base64
@@ -82,6 +91,33 @@  def npm_unpack(tarball, destdir, d):
     runfetchcmd(cmd, d, workdir=destdir)
     runfetchcmd("chmod -R +X '%s'" % (destdir), d, quiet=True, workdir=destdir)
 
+def create_npmrc_file(registry, namespace=None, username=None, password=None):
+    """Creates a npmrc file with authentication credentials populated based on provided username and password"""
+
+    # Create a temporary file we can store the npm authentication within
+    npmrc_file = tempfile.NamedTemporaryFile(mode="w", buffering=1).name
+
+    # Convert "username:password" string into base64 auth string
+    base64_auth_string = base64.b64encode(f"{username}:{password}".encode('utf-8')).decode('utf-8')
+
+    # Remove protocol from registry URL
+    registry = registry.replace('https://', '').replace('http://', '')
+
+    # Create the npmrc file with the authentication parameters
+    npm_auth_string = f"//{registry}/:_auth=\"{base64_auth_string}\""
+
+    with open(npmrc_file, 'w') as file:
+        file.write(npm_auth_string)
+        file.write("\n")
+
+        # Add namespace registry string if namespace is provided
+        if namespace:
+            npm_registry_string = f"@{namespace}:registry=https://{registry}"
+            file.write(npm_registry_string)
+            file.write("\n")
+
+    return npmrc_file
+
 class NpmEnvironment(object):
     """
     Using a npm config file seems more reliable than using cli arguments.
@@ -142,6 +178,9 @@  class Npm(FetchMethod):
         ud.package = None
         ud.version = None
         ud.registry = None
+        ud.username = None
+        ud.password = None
+        ud.namespace = None
 
         # Get the 'package' parameter
         if "package" in ud.parm:
@@ -154,6 +193,16 @@  class Npm(FetchMethod):
         if "version" in ud.parm:
             ud.version = ud.parm.get("version")
 
+        if "username" in ud.parm:
+            ud.username = ud.parm.get("username")
+
+        if "password" in ud.parm:
+            ud.password = ud.parm.get("password")
+
+        if "namespace" in ud.parm:
+            ud.namespace = ud.parm.get("namespace")
+            ud.package = f"@{ud.namespace}/{ud.package}"
+
         if not ud.version:
             raise MissingParameterError("Parameter 'version' required", ud.url)
 
@@ -189,7 +238,16 @@  class Npm(FetchMethod):
             args.append(("registry", ud.registry))
             pkgver = shlex.quote(ud.package + "@" + ud.version)
             cmd = ud.basecmd + " view %s" % pkgver
-            env = NpmEnvironment(d)
+
+            # Create an npmrc file if a username and password was supplied to the npm SRC_URI
+            npmrc_file = None
+            if (ud.username is not None and ud.password is not None):
+                try:
+                    npmrc_file = create_npmrc_file(ud.registry, ud.namespace, ud.username, ud.password)
+                except Exception as e:
+                    bb.warn(f"Error creating npmrc file: {e}")
+
+            env = NpmEnvironment(d, [], npmrc=npmrc_file)
             check_network_access(d, cmd, ud.registry)
             view_string = env.run(cmd, args=args)
 
@@ -224,6 +282,10 @@  class Npm(FetchMethod):
             uri = URI(tarball_url)
             uri.params["downloadfilename"] = ud.localfile
 
+            if (ud.username is not None and ud.password is not None):
+                uri.params["user"] = ud.username
+                uri.params["pswd"] = ud.password
+
             integrity = view.get("dist", {}).get("integrity")
             shasum = view.get("dist", {}).get("shasum")