Credentials Management
This example demonstrates how to manage authentication credentials for private NuGet package sources.
Overview
Credential management involves:
- Adding authentication for private package sources
- Managing usernames and passwords securely
- Handling different authentication methods
- Removing and updating credentials
- Best practices for credential security
Example 1: Basic Credential Management
Adding and managing basic credentials:
go
package main
import (
"fmt"
"log"
"os"
"github.com/scagogogo/nuget-config-parser/pkg/nuget"
)
func main() {
api := nuget.NewAPI()
config := api.CreateDefaultConfig()
fmt.Println("=== Basic Credential Management ===")
// Add private package sources
privateSources := []struct {
key string
url string
username string
password string
}{
{"company-private", "https://nuget.company.com/v3/index.json", "employee", "company_password"},
{"azure-artifacts", "https://pkgs.dev.azure.com/myorg/_packaging/myfeed/nuget/v3/index.json", "myuser", "pat_token"},
{"github-packages", "https://nuget.pkg.github.com/myorg/index.json", "github_user", "ghp_token"},
}
// Add sources and credentials
for _, source := range privateSources {
// Add the package source
api.AddPackageSource(config, source.key, source.url, "3")
fmt.Printf("Added source: %s\n", source.key)
// Add credentials for the source
api.AddCredential(config, source.key, source.username, source.password)
fmt.Printf(" - Added credentials for: %s\n", source.key)
}
// Verify credentials were added
fmt.Println("\n=== Credential Verification ===")
for _, source := range privateSources {
credential := api.GetCredential(config, source.key)
if credential != nil {
fmt.Printf("✅ %s has credentials configured\n", source.key)
// Display credential details (be careful with passwords!)
for _, cred := range credential.Add {
if cred.Key == "Username" {
fmt.Printf(" Username: %s\n", cred.Value)
} else if cred.Key == "ClearTextPassword" {
fmt.Printf(" Password: %s\n", maskPassword(cred.Value))
}
}
} else {
fmt.Printf("❌ %s missing credentials\n", source.key)
}
}
// Save configuration
err := api.SaveConfig(config, "CredentialsExample.Config")
if err != nil {
log.Fatalf("Failed to save config: %v", err)
}
fmt.Println("\nConfiguration with credentials saved!")
}
func maskPassword(password string) string {
if len(password) <= 4 {
return "****"
}
return password[:2] + "****" + password[len(password)-2:]
}
Example 2: Environment-Based Credentials
Using environment variables for secure credential management:
go
package main
import (
"fmt"
"log"
"os"
"github.com/scagogogo/nuget-config-parser/pkg/nuget"
)
func main() {
api := nuget.NewAPI()
config := api.CreateDefaultConfig()
fmt.Println("=== Environment-Based Credentials ===")
// Define sources with environment variable mappings
sources := []struct {
key string
url string
userEnvVar string
passEnvVar string
required bool
}{
{"company-feed", "https://nuget.company.com/v3/index.json", "COMPANY_NUGET_USER", "COMPANY_NUGET_PASS", true},
{"azure-devops", "https://pkgs.dev.azure.com/myorg/_packaging/feed/nuget/v3/index.json", "AZURE_DEVOPS_USER", "AZURE_DEVOPS_PAT", true},
{"github-packages", "https://nuget.pkg.github.com/myorg/index.json", "GITHUB_USER", "GITHUB_TOKEN", false},
{"private-registry", "https://private.registry.com/nuget", "REGISTRY_USER", "REGISTRY_PASS", false},
}
// Process each source
for _, source := range sources {
// Add the package source
api.AddPackageSource(config, source.key, source.url, "3")
fmt.Printf("Added source: %s\n", source.key)
// Get credentials from environment
username := os.Getenv(source.userEnvVar)
password := os.Getenv(source.passEnvVar)
if username != "" && password != "" {
// Add credentials
api.AddCredential(config, source.key, username, password)
fmt.Printf(" ✅ Added credentials from environment\n")
} else if source.required {
fmt.Printf(" ❌ Required credentials missing! Set %s and %s\n", source.userEnvVar, source.passEnvVar)
log.Fatalf("Missing required credentials for %s", source.key)
} else {
fmt.Printf(" ⚠️ Optional credentials not provided\n")
// Disable source if no credentials
api.DisablePackageSource(config, source.key)
}
}
// Display environment variable instructions
fmt.Println("\n=== Environment Variable Setup ===")
fmt.Println("To configure credentials, set these environment variables:")
for _, source := range sources {
status := "optional"
if source.required {
status = "required"
}
fmt.Printf(" %s (%s):\n", source.key, status)
fmt.Printf(" export %s=\"your_username\"\n", source.userEnvVar)
fmt.Printf(" export %s=\"your_password\"\n", source.passEnvVar)
fmt.Println()
}
// Save configuration
err := api.SaveConfig(config, "EnvCredentials.Config")
if err != nil {
log.Fatalf("Failed to save config: %v", err)
}
fmt.Println("Configuration saved with environment-based credentials!")
}
Example 3: Credential Updates and Removal
Managing credential lifecycle:
go
package main
import (
"fmt"
"log"
"github.com/scagogogo/nuget-config-parser/pkg/nuget"
)
func main() {
api := nuget.NewAPI()
// Load existing configuration
config, configPath, err := api.FindAndParseConfig()
if err != nil {
config = api.CreateDefaultConfig()
configPath = "NuGet.Config"
}
fmt.Printf("Managing credentials in: %s\n", configPath)
fmt.Println("=== Credential Lifecycle Management ===")
// Add initial credentials
api.AddPackageSource(config, "test-feed", "https://test.company.com/nuget", "3")
api.AddCredential(config, "test-feed", "old_user", "old_password")
fmt.Println("Added initial credentials")
// Display current credentials
displayCredentials(api, config, "test-feed")
// Update credentials (replace existing)
fmt.Println("\nUpdating credentials...")
api.AddCredential(config, "test-feed", "new_user", "new_password")
displayCredentials(api, config, "test-feed")
// Add another source with credentials
api.AddPackageSource(config, "temp-feed", "https://temp.company.com/nuget", "3")
api.AddCredential(config, "temp-feed", "temp_user", "temp_pass")
fmt.Println("\nAdded temporary source with credentials")
// List all sources with credentials
fmt.Println("\n=== All Sources with Credentials ===")
listAllCredentials(api, config)
// Remove credentials for specific source
fmt.Println("\nRemoving credentials for temp-feed...")
removed := api.RemoveCredential(config, "temp-feed")
if removed {
fmt.Println("✅ Credentials removed successfully")
} else {
fmt.Println("❌ No credentials found to remove")
}
// Verify removal
fmt.Println("\n=== After Credential Removal ===")
listAllCredentials(api, config)
// Clean up - remove sources without credentials
fmt.Println("\nCleaning up sources without credentials...")
cleanupSourcesWithoutCredentials(api, config)
// Save final configuration
err = api.SaveConfig(config, configPath)
if err != nil {
log.Fatalf("Failed to save config: %v", err)
}
fmt.Printf("\nFinal configuration saved to: %s\n", configPath)
}
func displayCredentials(api *nuget.API, config *types.NuGetConfig, sourceKey string) {
credential := api.GetCredential(config, sourceKey)
if credential != nil {
fmt.Printf("Credentials for %s:\n", sourceKey)
for _, cred := range credential.Add {
value := cred.Value
if cred.Key == "ClearTextPassword" || cred.Key == "Password" {
value = maskPassword(value)
}
fmt.Printf(" %s: %s\n", cred.Key, value)
}
} else {
fmt.Printf("No credentials found for %s\n", sourceKey)
}
}
func listAllCredentials(api *nuget.API, config *types.NuGetConfig) {
credentialCount := 0
for _, source := range config.PackageSources.Add {
credential := api.GetCredential(config, source.Key)
if credential != nil {
credentialCount++
fmt.Printf(" %s: Has credentials\n", source.Key)
} else {
fmt.Printf(" %s: No credentials\n", source.Key)
}
}
fmt.Printf("\nTotal sources with credentials: %d\n", credentialCount)
}
func cleanupSourcesWithoutCredentials(api *nuget.API, config *types.NuGetConfig) {
var sourcesToRemove []string
for _, source := range config.PackageSources.Add {
// Skip public sources that don't need credentials
if source.Key == "nuget.org" {
continue
}
credential := api.GetCredential(config, source.Key)
if credential == nil && isPrivateSource(source.Value) {
sourcesToRemove = append(sourcesToRemove, source.Key)
}
}
for _, sourceKey := range sourcesToRemove {
removed := api.RemovePackageSource(config, sourceKey)
if removed {
fmt.Printf(" Removed source without credentials: %s\n", sourceKey)
}
}
}
func isPrivateSource(url string) bool {
// Simple heuristic to identify private sources
privateIndicators := []string{
"company.com",
"internal",
"private",
"pkgs.dev.azure.com",
"nuget.pkg.github.com",
}
for _, indicator := range privateIndicators {
if strings.Contains(url, indicator) {
return true
}
}
return false
}
func maskPassword(password string) string {
if len(password) <= 4 {
return "****"
}
return password[:2] + "****" + password[len(password)-2:]
}
Example 4: Secure Credential Handling
Best practices for secure credential management:
go
package main
import (
"bufio"
"fmt"
"log"
"os"
"strings"
"syscall"
"golang.org/x/term"
"github.com/scagogogo/nuget-config-parser/pkg/nuget"
)
func main() {
api := nuget.NewAPI()
config := api.CreateDefaultConfig()
fmt.Println("=== Secure Credential Handling ===")
// Interactive credential setup
setupCredentialsInteractively(api, config)
// Validate credentials
validateCredentials(api, config)
// Save with warning about security
saveConfigSecurely(api, config)
}
func setupCredentialsInteractively(api *nuget.API, config *types.NuGetConfig) {
reader := bufio.NewReader(os.Stdin)
fmt.Println("Setting up private package source credentials...")
// Get source information
fmt.Print("Enter source name: ")
sourceName, _ := reader.ReadString('\n')
sourceName = strings.TrimSpace(sourceName)
fmt.Print("Enter source URL: ")
sourceURL, _ := reader.ReadString('\n')
sourceURL = strings.TrimSpace(sourceURL)
// Add the package source
api.AddPackageSource(config, sourceName, sourceURL, "3")
// Get credentials securely
fmt.Print("Enter username: ")
username, _ := reader.ReadString('\n')
username = strings.TrimSpace(username)
fmt.Print("Enter password (hidden): ")
passwordBytes, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
log.Fatalf("Failed to read password: %v", err)
}
password := string(passwordBytes)
fmt.Println() // New line after hidden input
// Add credentials
api.AddCredential(config, sourceName, username, password)
fmt.Printf("✅ Credentials added for source: %s\n", sourceName)
// Security reminder
fmt.Println("\n⚠️ Security Reminder:")
fmt.Println(" - Passwords are stored in plain text in the config file")
fmt.Println(" - Consider using environment variables for production")
fmt.Println(" - Ensure config file has appropriate permissions")
}
func validateCredentials(api *nuget.API, config *types.NuGetConfig) {
fmt.Println("\n=== Credential Validation ===")
for _, source := range config.PackageSources.Add {
credential := api.GetCredential(config, source.Key)
if credential != nil {
fmt.Printf("Validating credentials for: %s\n", source.Key)
var username, password string
for _, cred := range credential.Add {
if cred.Key == "Username" {
username = cred.Value
} else if cred.Key == "ClearTextPassword" {
password = cred.Value
}
}
// Basic validation
if username == "" {
fmt.Printf(" ❌ Username is empty\n")
} else {
fmt.Printf(" ✅ Username: %s\n", username)
}
if password == "" {
fmt.Printf(" ❌ Password is empty\n")
} else {
fmt.Printf(" ✅ Password: %s\n", maskPassword(password))
}
// Additional security checks
if len(password) < 8 {
fmt.Printf(" ⚠️ Password is shorter than 8 characters\n")
}
if password == "password" || password == "123456" {
fmt.Printf(" ❌ Password appears to be weak or default\n")
}
}
}
}
func saveConfigSecurely(api *nuget.API, config *types.NuGetConfig) {
configPath := "SecureCredentials.Config"
// Save configuration
err := api.SaveConfig(config, configPath)
if err != nil {
log.Fatalf("Failed to save config: %v", err)
}
// Set restrictive file permissions (Unix-like systems)
err = os.Chmod(configPath, 0600) // Read/write for owner only
if err != nil {
fmt.Printf("⚠️ Could not set restrictive permissions: %v\n", err)
} else {
fmt.Printf("✅ Set restrictive file permissions (600)\n")
}
fmt.Printf("Configuration saved securely to: %s\n", configPath)
// Security recommendations
fmt.Println("\n=== Security Recommendations ===")
fmt.Println("1. Use environment variables for credentials in production")
fmt.Println("2. Ensure config files have restrictive permissions")
fmt.Println("3. Consider using credential managers or key vaults")
fmt.Println("4. Regularly rotate passwords and tokens")
fmt.Println("5. Use personal access tokens instead of passwords when possible")
fmt.Println("6. Never commit config files with credentials to version control")
}
func maskPassword(password string) string {
if len(password) <= 4 {
return "****"
}
return password[:2] + "****" + password[len(password)-2:]
}
Key Concepts
Credential Types
- Username/Password: Traditional authentication
- Personal Access Tokens: Modern token-based auth
- API Keys: Service-specific authentication
- Encrypted Passwords: NuGet-encrypted credentials
Security Considerations
- Plain Text Storage: Credentials are stored in plain text
- File Permissions: Restrict access to config files
- Environment Variables: Use for sensitive credentials
- Version Control: Never commit credentials
- Token Rotation: Regularly update credentials
Best Practices
- Use Environment Variables: For production deployments
- Restrict File Permissions: Limit access to config files
- Use Tokens: Prefer PATs over passwords
- Regular Rotation: Update credentials periodically
- Credential Managers: Use system credential stores
- Separate Configs: Different configs for different environments
Next Steps
After mastering credential management:
- Learn about Config Options for global settings
- Explore Serialization for custom XML handling
- Study Position-Aware Editing for precise modifications
This guide provides comprehensive examples for managing NuGet package source credentials securely and effectively.