Skip to content

Latest commit

 

History

History
359 lines (298 loc) · 13 KB

README.md

File metadata and controls

359 lines (298 loc) · 13 KB

CatTree

A bash command to tree and recursively cat an entire directory. Extremely useful for working with AI.

cattree -h
Usage: cattree [-e extensions] [-d depth] [-a]
  -e, --extensions   Comma-separated list of file extensions to include (e.g., cs,json)
  -d, --depth        Depth level for the tree output and file search (e.g., 2)
  -a, --all          Include hidden directories and files (default is to exclude .vscode, .vs, bin, obj)
  -h, --help         Display this help message
❯ cattree -e "cs"

CatTreeing /Users/henrygetz/RiderProjects/WisarNet-JWT-Demo/WisarNetJwtDemo...

==================== ./Models/User.cs ====================

namespace WisarNetJwtDemo.Models
{
    public class User
    {
        // This property represents the unique identifier for the user in the database.
        public int Id { get; set; }

        // This property is typically used as a unique identifier for login purposes.
        public string Username { get; set; }

        // This property stores the user's password, which should be securely hashed before storage.
        public string Password { get; set; }

        // This stores the user's email address, used for communication or password recovery.
        public string Email { get; set; }

        // This list holds the roles assigned to the user, used for authorization.
        // It is initialized as a new list of strings, meaning it starts empty but ready to be added to.
        public List<string> Roles { get; set; } = new List<string>();
    }
}

==================== ./Models/Login.cs ====================

using System.ComponentModel.DataAnnotations;

namespace WisarNetJwtDemo.Models
{
    public class Login
    {
        // This property represents the username input from a user during the login process.
        // Usernames are essential for identifying the user in the system.
        // The Required attribute makes sure that the username field is not empty.
        // The StringLength attribute can be used to specify the maximum length of the username.
        [Required(ErrorMessage = "Username is required.")]
        [StringLength(100, ErrorMessage = "Username must be less than 100 characters.")]
        public string Username { get; set; }

        // This property is used to store the password input from a user during the login process.
        // Passwords need secure handling to prevent unauthorized access.
        // The Required attribute ensures the password field is not left empty.
        // The StringLength attribute can specify minimum and maximum password lengths, enhancing security.
        [Required(ErrorMessage = "Password is required.")]
        [StringLength(100, MinimumLength = 6, ErrorMessage = "Password must be between 6 and 100 characters.")]
        public string Password { get; set; }
    }
}

==================== ./Models/UserStore.cs ====================

namespace WisarNetJwtDemo.Models
{
    public class UserStore
    {
        // Declare a public static list of User objects named 'Users'.
        // Being static, this list is shared across all instances of the UserStore class and accessible without creating an instance of the class.
        // This list is initialized with predefined user data.
        public static List<User> Users = new List<User>
        {
            new User { Id=1, Username = "admin", Password = "password", Email="[email protected]", Roles = new List<string> { "Admin", "User" } },
            new User { Id=2, Username = "user", Password = "password", Email="[email protected]", Roles = new List<string> { "User" } },
            new User { Id=3, Username = "test", Password = "password", Email="[email protected]", Roles = new List<string> { "Admin" } }
        };
    }
}

==================== ./Controllers/ResourceController.cs ====================

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WisarNetJwtDemo.Models;

namespace WisarNetJwtDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ResourceController : ControllerBase
    {
        // Static list to simulate database of resources.
        private static List<User> _users = new List<User>
        {
            new User { Id = 1, Username = "admin", Email = "[email protected]" },
            new User { Id = 2, Username = "user", Email = "[email protected]" },
            new User { Id = 3, Username = "test", Email = "[email protected]" }
        };

        // GET method to retrieve all users.
        [HttpGet]
        [Authorize]  // To secure the endpoints with JWT authentication
        public ActionResult<List<User>> GetAllUsers()
        {
            return _users;
        }

        // GET method with a parameter to retrieve a specific user by their ID.
        [HttpGet("{id}")]
        [Authorize]
        public ActionResult<User> GetUser(int id)
        {
            var user = _users.FirstOrDefault(u => u.Id == id);
            if (user == null)
                return NotFound();
            return user;
        }

        // POST method to create a new user.
        [HttpPost]
        [Authorize]
        public ActionResult<User> CreateUser([FromBody] User user)
        {
            _users.Add(user);
            return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
        }

        // PUT method to update an existing user.
        [HttpPut("{id}")]
        [Authorize]
        public ActionResult<User> UpdateUser(int id, [FromBody] User user)
        {
            var index = _users.FindIndex(u => u.Id == id);
            if (index == -1)
                return NotFound();

            _users[index] = user;
            return NoContent();
        }

        // DELETE method to remove a user by ID.
        [HttpDelete("{id}")]
        [Authorize]
        public IActionResult DeleteUser(int id)
        {
            var index = _users.FindIndex(u => u.Id == id);
            if (index == -1)
                return NotFound();

            _users.RemoveAt(index);
            return NoContent();
        }
    }
}


==================== ./Controllers/UsersController.cs ====================

using WisarNetJwtDemo.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace JWTAuthServer.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AuthController : ControllerBase
    {
        // Private field to hold the configuration settings injected through the constructor.
        private readonly IConfiguration _configuration;

        // Constructor that accepts IConfiguration and initializes the _configuration field.
        public AuthController(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        // Action method responding to POST requests on "api/Users/Login".
        // Expects a Login model in the request body.
        [HttpPost("Login")]
        public IActionResult Login([FromBody] Login request)
        {
            // Checks if the provided model (request) is valid based on data annotations in the Login model.
            if (ModelState.IsValid)
            {
                // Searches for a user in a predefined user store that matches both username and password.
                var user = UserStore.Users.FirstOrDefault(u => u.Username == request.Username && u.Password == request.Password);

                // Checks if the user object is null, which means no matching user was found.
                if (user == null)
                {
                    // Returns a 401 Unauthorized response with a custom message.
                    return Unauthorized("Invalid user credentials.");
                }

                // Calls a method to generate a JWT token for the authenticated user.
                var token = IssueToken(user);

                // Returns a 200 OK response, encapsulating the JWT token in an anonymous object.
                return Ok(new { Token = token });
            }

            // If the model state is not valid, returns a 400 Bad Request response with a custom message.
            return BadRequest("Invalid Request Body");
        }

        // Private method to generate a JWT token using the user's data.
        private string IssueToken(User user)
        {
            // Creates a new symmetric security key from the JWT key specified in the app configuration.
            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));
            // Sets up the signing credentials using the above security key and specifying the HMAC SHA256 algorithm.
            var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

            // Defines a set of claims to be included in the token.
            var claims = new List<Claim>
            {
                // Custom claim using the user's ID.
                new Claim("Myapp_User_Id", user.Id.ToString()),
                // Standard claim for user identifier, using username.
                new Claim(ClaimTypes.NameIdentifier, user.Username),
                // Standard claim for user's email.
                new Claim(ClaimTypes.Email, user.Email),
                // Standard JWT claim for subject, using user ID.
                new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString())
            };

            // Adds a role claim for each role associated with the user.
            user.Roles.ForEach(role => claims.Add(new Claim(ClaimTypes.Role, role)));

            // Creates a new JWT token with specified parameters including issuer, audience, claims, expiration time, and signing credentials.
            var token = new JwtSecurityToken(
                issuer: _configuration["Jwt:Issuer"],
                audience: _configuration["Jwt:Audience"],
                claims: claims,
                expires: DateTime.Now.AddHours(1), // Token expiration set to 1 hour from the current time.
                signingCredentials: credentials);

            // Serializes the JWT token to a string and returns it.
            return new JwtSecurityTokenHandler().WriteToken(token);
        }
    }
}

==================== ./Program.cs ====================

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models; // Make sure to include this namespace for OpenApi* classes
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.PropertyNamingPolicy = null; // Keep JSON properties as is
    });

// Configure JWT authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
        };
    });

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "WisarNetJwtDemo", Version = "v1" });

    // Add JWT Authentication to Swagger
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Name = "Authorization",
        Type = SecuritySchemeType.Http,
        Scheme = "Bearer",
        BearerFormat = "JWT",
        In = ParameterLocation.Header,
        Description = "Enter 'Bearer' [space] and then your valid token in the text input below.\r\n\r\nExample: \"Bearer 12345abcdef\"",
    });

    c.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                }
            },
            Array.Empty<string>()
        }
    });
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

// Add authentication and authorization middleware
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();


==================== Cat Tree ====================

 /\_/\
( o.o )
 > ^ <
   .
   ├── Controllers
   │   ├── ResourceController.cs
   │   └── UsersController.cs
   ├── Models
   │   ├── Login.cs
   │   ├── User.cs
   │   └── UserStore.cs
   └── Program.cs

   3 directories, 6 files



  ~/RiderProjects/WisarNet-JWT-Demo/WisarNetJwtDemo on   master +1 !1                                                                                           base at  09:27:53 PM
❯