Skip to content

Commit

Permalink
Updated Security
Browse files Browse the repository at this point in the history
-- WEB API --
-Added Csrf validation on unauthorized endpoints
-Added XsrfTokenController

-- WEB CLIENT --
-Added Xsrf service
-Updated all @click with .prevent extension
-Updated logout redirect url
-Set up xsrf service in api-service
  • Loading branch information
jioo committed Sep 12, 2018
1 parent 2834ec2 commit 8873d55
Show file tree
Hide file tree
Showing 21 changed files with 108 additions and 53 deletions.
7 changes: 4 additions & 3 deletions WebApi/Controllers/AccountsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
namespace WebApi.Controllers
{
[Authorize(Roles = "Admin")]
[Route("api/[controller]")]
[Route("api/[controller]"), ApiController]
public class AccountsController : ControllerBase
{
private readonly UserManager<User> _manager;
Expand All @@ -35,7 +35,7 @@ public AccountsController(

// POST: api/accounts/register
[HttpPost("register")]
public async Task<IActionResult> Register([FromBody]RegisterViewModel model)
public async Task<IActionResult> Register(RegisterViewModel model)
{
var isCardExist = await _service.isCardExist(Guid.Empty, model.CardNo);
if (isCardExist)
Expand Down Expand Up @@ -64,7 +64,8 @@ public async Task<IActionResult> Register([FromBody]RegisterViewModel model)
Identity = user,
FullName = model.FullName,
CardNo = model.CardNo,
Position = model.Position
Position = model.Position,
Status = Status.Active
});
return new OkObjectResult(syncResult);
}
Expand Down
11 changes: 6 additions & 5 deletions WebApi/Controllers/AuthController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

namespace WebApi.Controllers
{
[Route("api/[controller]")]
[Route("api/[controller]"), ApiController]
public class AuthController : ControllerBase
{
private readonly UserManager<User> _userManager;
Expand All @@ -37,7 +37,8 @@ public AuthController(

// POST api/auth/login
[HttpPost("login")]
public async Task<IActionResult> Login([FromBody]LoginViewModel model)
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model)
{
// Check if password is correct
var user = await _userManager.FindByNameAsync(model.UserName);
Expand All @@ -59,24 +60,24 @@ public async Task<IActionResult> Login([FromBody]LoginViewModel model)
}

// POST api/auth/check
[HttpGet("check")]
[Authorize]
[HttpGet("check")]
public IActionResult Check()
{
return Ok();
}

// POST api/auth/is-admin
[HttpGet("is-admin")]
[Authorize(Roles = "Admin")]
[HttpGet("is-admin")]
public IActionResult IsAdmin()
{
return Ok();
}

// POST api/auth/is-employee
[HttpGet("is-employee")]
[Authorize(Roles = "Employee")]
[HttpGet("is-employee")]
public IActionResult IsEmployee()
{
return Ok();
Expand Down
3 changes: 1 addition & 2 deletions WebApi/Controllers/ConfigController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
namespace WebApi.Controllers
{
[Authorize(Roles = "Admin")]
[Route("api/[controller]")]
[ApiController]
[Route("api/[controller]"), ApiController]
public class ConfigController : ControllerBase
{
private readonly IConfigService _service;
Expand Down
4 changes: 2 additions & 2 deletions WebApi/Controllers/EmployeeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
namespace WebApi.Controllers
{
[Authorize(Roles = "Admin")]
[Route("api/[controller]")]
[ApiController]
[Route("api/[controller]"), ApiController]
public class EmployeeController : ControllerBase
{
private readonly IEmployeeService _service;
Expand Down Expand Up @@ -46,6 +45,7 @@ public async Task<IActionResult> Find(Guid id)

// PUT api/employee
[HttpPut]
[AllowAnonymous]
public async Task<IActionResult> Update(EmployeeViewModel model)
{
// Check if Card No already exist
Expand Down
12 changes: 6 additions & 6 deletions WebApi/Controllers/LogController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
namespace WebApi.Controllers
{
[Authorize]
[Route("api/[controller]")]
[ApiController]
[Route("api/[controller]"), ApiController]
public class LogController : ControllerBase
{
private JsonSerializerSettings settings = new JsonSerializerSettings { Formatting = Formatting.Indented, ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
Expand All @@ -40,9 +39,10 @@ public async Task<IActionResult> Index()
}

// POST api/log
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Log([FromBody] LogInOutViewModel model)
[ValidateAntiForgeryToken]
[HttpPost]
public async Task<IActionResult> Log(LogInOutViewModel model)
{
// Validate card no. & password
var user = await _service.ValidateTimeInOutCredentials(model);
Expand All @@ -54,9 +54,9 @@ public async Task<IActionResult> Log([FromBody] LogInOutViewModel model)
}

// PUT api/log
[HttpPut]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Update([FromBody]LogEditViewModel model)
[HttpPut]
public async Task<IActionResult> Update(LogEditViewModel model)
{
return new OkObjectResult(await _service.UpdateAsync(model));
}
Expand Down
27 changes: 27 additions & 0 deletions WebApi/Controllers/XsrfTokenController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Mvc;

namespace WebApi.Controllers
{
[Route("api/[controller]"), ApiController]
public class XsrfTokenController : ControllerBase
{
private readonly IAntiforgery _antiforgery;

public XsrfTokenController(IAntiforgery antiforgery)
{
_antiforgery = antiforgery;
}

[HttpGet]
public IActionResult Get()
{
var tokens = _antiforgery.GetAndStoreTokens(HttpContext);

return new ObjectResult(new {
token = tokens.RequestToken,
tokenName = tokens.HeaderName
});
}
}
}
2 changes: 0 additions & 2 deletions WebApi/Services/LogService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ public async Task<IList<LogViewModel>> GetAllAsync()
return await _repoLog.Context.Query()
.Where(m => m.Deleted == null)
.OrderByDescending(m => m.Created)
.Include(m => m.Employee)
.Select(m => new LogViewModel
{
Id = m.Id,
Expand Down Expand Up @@ -71,7 +70,6 @@ public async Task<LogViewModel> FindAsync(Guid id)
{
return await _repoLog.Context.Query()
.Where(m => m.Id == id)
.Include(m => m.Employee)
.Select(m => new LogViewModel
{
Id = m.Id,
Expand Down
38 changes: 24 additions & 14 deletions WebApi/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
using WebApi.Repositories;
using WebApi.Helpers;
using Hubs.BroadcastHub;
using Microsoft.AspNetCore.Antiforgery;

namespace WebApi
{
Expand Down Expand Up @@ -106,28 +107,29 @@ public void ConfigureServices(IServiceCollection services)
});

// Add Identity
var builder = services.AddIdentityCore<User>(o =>
services.AddIdentityCore<User>(o =>
{
// configure identity options
// Configure identity options
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonAlphanumeric = false;
o.Password.RequiredLength = 6;
}).AddRoles<IdentityRole>();
builder = new IdentityBuilder(builder.UserType, typeof(IdentityRole), builder.Services);
builder.AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();
})
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();

services.AddAutoMapper();
services.AddMvc(options =>
{
// Add automatic model validation
options.Filters.Add(typeof(ValidateModelStateAttribute));
// options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

// services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
// X-CSRF-Token
services.AddAntiforgery(options=>
{
options.HeaderName = "X-XSRF-Token";
options.SuppressXFrameOptionsHeader = false;
});

services.AddCors();
services.AddSignalR();

Expand Down Expand Up @@ -192,6 +194,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, IService
});
});

// Enable CORS
app.UseCors(builder =>
builder.AllowAnyOrigin()
.AllowAnyHeader()
Expand All @@ -201,25 +204,32 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, IService

app.UseAuthentication();
app.UseMvc();

app.Use(async (context, next) =>
{
await next();
await next();
if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value))
{
context.Request.Path = "/index.html";
await next();
}
});

// Single Page Application set up
app.UseDefaultFiles();
app.UseStaticFiles();

// Set up SignalR Hubs
app.UseSignalR(routes =>
{
routes.MapHub<BroadcastHub>("/broadcast");
});

// Identity user seed
CreateUsersAndRoles(services).Wait();

// Default Attendance Configuration
AttendanceConfiguration(services).Wait();
}

Expand Down
2 changes: 1 addition & 1 deletion WebClient/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>BDF Attendance System</title>
<title>Attendance System</title>
</head>
<body>
<div id="app"></div>
Expand Down
2 changes: 1 addition & 1 deletion WebClient/src/components/change-password.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="orange" :loading="isLoading" @click="changePassword" >Update</v-btn>
<v-btn color="orange" :loading="isLoading" @click.prevent="changePassword" >Update</v-btn>
</v-card-actions>
</v-card>
</template>
Expand Down
2 changes: 1 addition & 1 deletion WebClient/src/components/employee-create.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="error" :loading="isLoading" @click.native="dialog = false">Cancel</v-btn>
<v-btn color="success" :loading="isLoading" @click="create">Save</v-btn>
<v-btn color="success" :loading="isLoading" @click.prevent="create">Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
Expand Down
2 changes: 1 addition & 1 deletion WebClient/src/components/employee-edit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="error" :loading="isLoading" @click.native="dialog = false">Cancel</v-btn>
<v-btn color="success" :loading="isLoading" @click="edit">Update</v-btn>
<v-btn color="success" :loading="isLoading" @click.prevent="edit">Update</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
Expand Down
2 changes: 1 addition & 1 deletion WebClient/src/components/employee-list.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<td>{{ props.item.position }}</td>
<td>{{ props.item.cardNo }}</td>
<td>
<v-btn icon class="mx-0" @click="openDialog(props.item.id)">
<v-btn icon class="mx-0" @click.prevent="openDialog(props.item.id)">
<v-icon color="teal">edit</v-icon>
</v-btn>
</td>
Expand Down
2 changes: 1 addition & 1 deletion WebClient/src/components/log-edit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="error" :loading="isLoading" @click.native="dialog = false">Cancel</v-btn>
<v-btn color="success" :loading="isLoading" @click="edit">Update</v-btn>
<v-btn color="success" :loading="isLoading" @click.prevent="edit">Update</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
Expand Down
10 changes: 5 additions & 5 deletions WebClient/src/components/log-employee-form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
v-model="form.cardno" required :rules="[required]" v-on:keyup.enter="$refs.password.focus()"></v-text-field>

<v-text-field prepend-icon="lock" label="Password" type="password" color="orange" ref="password"
v-model="form.password" required :rules="[required]" v-on:keyup.enter="logEmp()"></v-text-field>
v-model="form.password" required :rules="[required]" v-on:keyup.enter.prevent="logEmp()"></v-text-field>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="orange" :loading="isLoading" @click="logEmp">Login</v-btn>
<v-btn color="orange" :loading="isLoading" @click.prevent="logEmp">Login</v-btn>
</v-card-actions>
</v-card>
</template>
Expand Down Expand Up @@ -49,11 +49,11 @@ export default {
this.$store.dispatch(LOG_EMPLOYEE, JSON.stringify(this.form)).then((res) => {
this.resetForm()
this.$refs.cardNo.focus()
const notifDuration = 4000
const notifDuration = 8000
if (res.timeOut === '' ) {
this.$notify({ type: 'success', text: 'Welcome '+ res.fullName+ '!<br /> Time in: ' + res.timeIn, duration: notifDuration })
this.$notify({ type: 'success', text: `Welcome ${res.fullName}!<br/> Time in: ${res.timeIn}`, duration: notifDuration })
} else {
this.$notify({ type: 'success', text: 'Time out: ' + res.timeIn, duration: notifDuration })
this.$notify({ type: 'warning', text: `Goodbye ${res.fullName}!<br/> Time out: ${res.timeIn}`, duration: notifDuration })
}
})
}
Expand Down
2 changes: 1 addition & 1 deletion WebClient/src/components/log-list.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export default {
if(this.isRole('Employee')) {
const userId = this.currentUser.user.empId
this.items = this.logs.filter(m => m.EmployeeId == userId)
this.items = this.logs.filter(m => m.employeeId === userId)
this.headers = [
{ text: "Time In", value: "timeIn" },
{ text: "Time Out", value: "timeOut" }
Expand Down
6 changes: 3 additions & 3 deletions WebClient/src/components/login-form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
v-model="form.username" required :rules="[required]"></v-text-field>

<v-text-field prepend-icon="lock" label="Password" type="password" color="orange"
v-model="form.password" required :rules="[required]"></v-text-field>
v-model="form.password" required :rules="[required]" v-on:keyup.enter.prevent="loginUser()"></v-text-field>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="orange" @click="loginUser">Login</v-btn>
<v-btn color="orange" :loading="isLoading" @click.prevent="loginUser">Login</v-btn>
</v-card-actions>
</v-card>
</template>
Expand All @@ -37,7 +37,7 @@ export default {
},
computed: {
...mapGetters(["currentUser"])
...mapGetters(["currentUser", "isLoading"])
},
methods: {
Expand Down
Loading

0 comments on commit 8873d55

Please sign in to comment.