Skip to content

Commit 7a1fe29

Browse files
author
Oyvind Timian Dokk Husveg
committed
Finished endpoints and tests
1 parent c749147 commit 7a1fe29

File tree

8 files changed

+437
-245
lines changed

8 files changed

+437
-245
lines changed

exercise.tests/IntegrationTests/PostTests.cs

Lines changed: 326 additions & 196 deletions
Large diffs are not rendered by default.

exercise.wwwapi/DTOs/Posts/CreatePostCommentDTO.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace exercise.wwwapi.DTOs.Posts
44
{
55
public class CreatePostCommentDTO
66
{
7-
public required int Userid { get; set; }
7+
//public required int Userid { get; set; }
88
public required string Content { get; set; }
99
}
1010
}

exercise.wwwapi/DTOs/Posts/CreatePostDTO.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ namespace exercise.wwwapi.DTOs.Posts
77
{
88
public class CreatePostDTO
99
{
10-
public required int Userid { get; set; }
1110
public required string Content { get; set; }
1211

1312
}

exercise.wwwapi/Data/PersonData.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public class PersonData
6565
{"abstract", 51 }
6666
};
6767
private List<string> _passwordHashes = new List<string> {
68-
"$2a$11$mbfii1SzR9B7ZtKbYydLOuAPBSA2ziAP0CrsdU8QgubGo2afw7Wuy", // Timianerkul1!
68+
"$2a$11$mbfii1SzR9B7ZtKbYydLOuAPBSA2ziAP0CrsdU8QgubGo2afw7Wuy", // Timianerkul1! //apparently this hash isnt right...
6969
"$2a$11$5ttNr5DmMLFlyVVv7PFkQOhIstdGTBmSdhMHaQcUOZ8zAgsCqFT6e", // SuperHash!4
7070
"$2a$11$KBLC6riEn/P78lLCwyi0MO9DrlxapLoGhCfdgXwLU2s44P.StKO/6", // Neidintulling!l33t
7171
"$2a$11$DFMtyLv243uk2liVbzCxXeshisouexhitDg5OUuBU.4LVn//QG5O." // lettPassord123!

exercise.wwwapi/Data/PostData.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,11 @@ public class PostData
5757
};
5858
DateTime somedate = new DateTime(2020, 12, 05, 0, 0, 0, DateTimeKind.Utc);
5959
private List<Post> _posts = new List<Post>();
60-
private int numusers = 300;
6160
public PostData(List<User> users)
6261
{
6362
Random random = new Random(1);
6463

65-
for (int i = 1; i < 25; i++)
64+
for (int i = 1; i < users.Count / 5; i++)
6665
{
6766
string subject = _subject[random.Next(9-1)];
6867
string obj = _objects[random.Next(16-1)];

exercise.wwwapi/Endpoints/PostEndpoints.cs

Lines changed: 84 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
using exercise.wwwapi.DTOs;
44
using exercise.wwwapi.DTOs.GetUsers;
55
using exercise.wwwapi.DTOs.Posts;
6+
using exercise.wwwapi.Helpers;
67
using exercise.wwwapi.Models;
78
using exercise.wwwapi.Repository;
89
using Microsoft.AspNetCore.Authorization;
910
using Microsoft.AspNetCore.Mvc;
1011
using Microsoft.EntityFrameworkCore;
12+
using Microsoft.Extensions.Hosting;
1113
using System.Security.Claims;
1214

1315
namespace exercise.wwwapi.Endpoints
@@ -19,63 +21,70 @@ public static void ConfigurePostEndpoints(this WebApplication app)
1921
var posts = app.MapGroup("posts");
2022
posts.MapPost("/", CreatePost).WithSummary("Create post");
2123
posts.MapGet("/", GetAllPosts).WithSummary("Get all posts");
22-
posts.MapPatch("/{id}", UpdatePost).WithSummary("Update a certain post");
23-
posts.MapDelete("/{id}", DeletePost).WithSummary("Remove a certain post");
24+
posts.MapPatch("/{postid}", UpdatePost).WithSummary("Update a certain post");
25+
posts.MapDelete("/{postid}", DeletePost).WithSummary("Remove a certain post");
2426

2527
posts.MapPost("/{postId}/comments", AddCommentToPost).WithSummary("Add a new comment to a post");
2628
posts.MapGet("/{postId}/comments", GetCommentsForPost).WithSummary("Get comments for a specific post");
2729

2830
// Standalone comment endpoints for editing/deleting
2931
var comments = app.MapGroup("comments");
30-
comments.MapPatch("/{id}", UpdateComment).WithSummary("Edit an existing comment");
31-
comments.MapDelete("/{id}", DeleteCommentById).WithSummary("Remove an existing comment");
32+
comments.MapPatch("/{commentId}", UpdateComment).WithSummary("Edit an existing comment");
33+
comments.MapDelete("/{commentId}", DeleteCommentById).WithSummary("Remove an existing comment");
3234

3335
// Endpoints to get by user
3436
posts.MapGet("/user/{userId}", GetPostsByUser).WithSummary("Get posts by a specific user");
3537
comments.MapGet("/user/{userId}", GetCommentsByUser).WithSummary("Get comments by a specific user");
3638
}
3739

38-
40+
[Authorize]
3941
[ProducesResponseType(StatusCodes.Status200OK)]
4042
[ProducesResponseType(StatusCodes.Status404NotFound)]
4143
[ProducesResponseType(StatusCodes.Status400BadRequest)]
42-
public static IResult CreatePost(IRepository<User> userservice, IRepository<Post> postservice, IMapper mapper, CreatePostDTO request)
44+
public static IResult CreatePost(
45+
IRepository<User> userservice,
46+
IRepository<Post> postservice,
47+
IMapper mapper,
48+
ClaimsPrincipal user,
49+
CreatePostDTO request
50+
)
4351
{
44-
45-
User? user = userservice.GetById(request.Userid);
52+
int? userid = user.UserRealId();
53+
User? dbUser = userservice.GetById(userid);
4654
if (user == null)
4755
return Results.NotFound(new ResponseDTO<Object>{Message = "Invalid userID"});
4856

4957
if (string.IsNullOrWhiteSpace(request.Content))
5058
return Results.BadRequest(new ResponseDTO<Object> { Message = "Content cannot be empty" });
5159

52-
Post post = new Post() { CreatedAt = DateTime.UtcNow, NumLikes = 0, UserId=request.Userid, Content=request.Content };
60+
Post post = new Post() { CreatedAt = DateTime.UtcNow, NumLikes = 0, UserId= dbUser.Id, Content=request.Content };
5361

5462
// is a try catch needed here?
5563
postservice.Insert(post);
5664
postservice.Save();
5765

5866

59-
UserBasicDTO userBasicDTO = mapper.Map<UserBasicDTO>(user);
67+
UserBasicDTO userBasicDTO = mapper.Map<UserBasicDTO>(dbUser);
6068
PostDTO postDTO = mapper.Map<PostDTO>(post);
6169
postDTO.User = userBasicDTO;
6270

6371
ResponseDTO<PostDTO> response = new ResponseDTO<PostDTO>
6472
{
65-
Message = "success",
73+
Message = "Success",
6674
Data = postDTO
6775
};
6876

6977
return Results.Created($"/posts/{post.Id}", response);
7078
}
79+
[Authorize]
7180
[ProducesResponseType(StatusCodes.Status200OK)]
7281
public static IResult GetAllPosts(IRepository<Post> service, IMapper mapper)
7382
{
7483
IEnumerable<Post> results = service.GetWithIncludes(q => q.Include(p => p.User).Include(p => p.Comments).ThenInclude(c => c.User));
7584
IEnumerable<PostDTO> postDTOs = mapper.Map<IEnumerable<PostDTO>>(results);
7685
ResponseDTO<IEnumerable<PostDTO>> response = new ResponseDTO<IEnumerable<PostDTO>>()
7786
{
78-
Message = "success",
87+
Message = "Success",
7988
Data = postDTOs
8089
};
8190
return TypedResults.Ok(response);
@@ -84,28 +93,25 @@ public static IResult GetAllPosts(IRepository<Post> service, IMapper mapper)
8493
[Authorize]
8594
[ProducesResponseType(StatusCodes.Status200OK)]
8695
[ProducesResponseType(StatusCodes.Status400BadRequest)]
87-
public static IResult UpdatePost(IRepository<Post> service, IMapper mapper, ClaimsPrincipal user, int id, UpdatePostDTO request)
96+
[ProducesResponseType(StatusCodes.Status403Forbidden)]
97+
98+
public static IResult UpdatePost(IRepository<Post> service, IMapper mapper, ClaimsPrincipal user, int postid, UpdatePostDTO request)
8899
{
89100
if (string.IsNullOrWhiteSpace(request.Content)) return TypedResults.BadRequest(new ResponseDTO<object>{
90101
Message = "Content cannot be empty"
91102
});
92103

93-
Post? post = service.GetById(id, q=>q.Include(p => p.User));
94-
95-
104+
Post? post = service.GetById(postid, q=>q.Include(p => p.User));
96105

97106
if (post == null) return TypedResults.NotFound(new ResponseDTO<Object> { Message = "Post not found" });
98107

99-
var currentUserId = user.FindFirstValue(ClaimTypes.NameIdentifier);
100-
if (post.UserId.ToString() != currentUserId)
108+
Console.WriteLine($"Role:{user.Role()} {Roles.student}| {post.UserId} {user.UserRealId()}");
109+
if (post.UserId != user.UserRealId() && user.Role() == (int)Roles.student)
101110
{
102-
// Create your custom response object
103111
var forbiddenResponse = new ResponseDTO<object>
104112
{
105113
Message = "You are not authorized to edit this post."
106114
};
107-
108-
// Return it as JSON with a 403 Forbidden status code
109115
return TypedResults.Json(forbiddenResponse, statusCode: StatusCodes.Status403Forbidden);
110116
}
111117

@@ -122,23 +128,41 @@ public static IResult UpdatePost(IRepository<Post> service, IMapper mapper, Clai
122128
return TypedResults.Ok(new ResponseDTO<PostDTO> { Message = "Success", Data = postDTO });
123129
}
124130

131+
[Authorize]
125132
[ProducesResponseType(StatusCodes.Status200OK)]
126133
[ProducesResponseType(StatusCodes.Status404NotFound)]
127-
private static IResult DeletePost(IRepository<Post> service, int id)
134+
[ProducesResponseType(StatusCodes.Status403Forbidden)]
135+
private static IResult DeletePost(IRepository<Post> service, ClaimsPrincipal user, int postid)
128136
{
129-
Post? post = service.GetById(id, q => q.Include(p => p.User).Include(p => p.Comments).ThenInclude(c => c.User));
137+
Post? post = service.GetById(postid, q => q.Include(p => p.User).Include(p => p.Comments).ThenInclude(c => c.User));
130138
if (post == null) return TypedResults.NotFound(new ResponseDTO<Object> { Message = "Post not found" });
131139

132-
service.Delete(id);
140+
if (user.Role() == (int)Roles.student && post.UserId != user.UserRealId())
141+
{
142+
var forbiddenResponse = new ResponseDTO<object>
143+
{
144+
Message = "You are not authorized to delete this post."
145+
};
146+
return TypedResults.Json(forbiddenResponse, statusCode: StatusCodes.Status403Forbidden);
147+
}
148+
service.Delete(postid);
133149
service.Save();
134150

135151
return TypedResults.Ok(new ResponseDTO<PostDTO> { Message = "Success" });
136152
}
137153

154+
[Authorize]
138155
[ProducesResponseType(StatusCodes.Status201Created)]
139156
[ProducesResponseType(StatusCodes.Status404NotFound)]
140157
[ProducesResponseType(StatusCodes.Status400BadRequest)]
141-
private static IResult AddCommentToPost(IRepository<PostComment> commentService, IRepository<Post> postService, IRepository<User> userService, IMapper mapper, int postId, CreatePostCommentDTO request)
158+
private static IResult AddCommentToPost(
159+
IRepository<PostComment> commentService,
160+
IRepository<Post> postService,
161+
IRepository<User> userService,
162+
IMapper mapper,
163+
ClaimsPrincipal user,
164+
int postId,
165+
CreatePostCommentDTO request)
142166
{
143167
// Check if post exists
144168
var post = postService.GetById(postId);
@@ -148,8 +172,8 @@ private static IResult AddCommentToPost(IRepository<PostComment> commentService,
148172
}
149173

150174
// Check if user exists
151-
var user = userService.GetById(request.Userid);
152-
if (user == null)
175+
var dbUser = userService.GetById(user.UserRealId());
176+
if (dbUser == null)
153177
{
154178
return TypedResults.NotFound(new ResponseDTO<object> { Message = "User not found." });
155179
}
@@ -163,7 +187,7 @@ private static IResult AddCommentToPost(IRepository<PostComment> commentService,
163187
var comment = new PostComment
164188
{
165189
Content = request.Content,
166-
UserId = request.Userid,
190+
UserId = dbUser.Id,
167191
PostId = postId,
168192
CreatedAt = DateTime.UtcNow
169193
};
@@ -177,6 +201,7 @@ private static IResult AddCommentToPost(IRepository<PostComment> commentService,
177201
return TypedResults.Created($"/comments/{comment.Id}", new ResponseDTO<PostCommentDTO> { Message = "Success", Data = commentDto });
178202
}
179203

204+
[Authorize]
180205
[ProducesResponseType(StatusCodes.Status200OK)]
181206
private static IResult GetCommentsForPost(IRepository<Post> postservice, IMapper mapper, int postId)
182207
{
@@ -190,17 +215,29 @@ private static IResult GetCommentsForPost(IRepository<Post> postservice, IMapper
190215
[ProducesResponseType(StatusCodes.Status200OK)]
191216
[ProducesResponseType(StatusCodes.Status404NotFound)]
192217
[ProducesResponseType(StatusCodes.Status400BadRequest)]
193-
private static IResult UpdateComment(IRepository<PostComment> service, IMapper mapper, int id, CreatePostCommentDTO request)
218+
private static IResult UpdateComment(
219+
IRepository<PostComment> service,
220+
IMapper mapper,
221+
ClaimsPrincipal user,
222+
int commentId,
223+
CreatePostCommentDTO request)
194224
{
195225
if (string.IsNullOrWhiteSpace(request.Content))
196226
{
197227
return TypedResults.BadRequest(new ResponseDTO<object> { Message = "Content cannot be empty." });
198228
}
199229

200-
var comment = service.GetById(id, q => q.Include(c => c.User));
201-
if (comment == null)
230+
var comment = service.GetById(commentId, q => q.Include(c => c.User));
231+
if (comment == null) return TypedResults.NotFound(new ResponseDTO<object> { Message = "Comment not found." });
232+
233+
//Console.WriteLine($"Role:{user.Role()} {Roles.student.ToString()}| {comment.UserId} {user.UserRealId()}");
234+
if (comment.UserId != user.UserRealId() && user.Role() == (int)Roles.student)
202235
{
203-
return TypedResults.NotFound(new ResponseDTO<object> { Message = "Comment not found." });
236+
var forbiddenResponse = new ResponseDTO<object>
237+
{
238+
Message = "You are not authorized to edit this comment."
239+
};
240+
return TypedResults.Json(forbiddenResponse, statusCode: StatusCodes.Status403Forbidden);
204241
}
205242

206243
comment.Content = request.Content;
@@ -213,22 +250,33 @@ private static IResult UpdateComment(IRepository<PostComment> service, IMapper m
213250
return TypedResults.Ok(new ResponseDTO<PostComment> { Message = "Comment updated successfully.", Data = commentDto });
214251
}
215252

253+
[Authorize]
216254
[ProducesResponseType(StatusCodes.Status200OK)]
217255
[ProducesResponseType(StatusCodes.Status404NotFound)]
218-
private static IResult DeleteCommentById(IRepository<PostComment> service, int id)
256+
private static IResult DeleteCommentById(IRepository<PostComment> service, ClaimsPrincipal user, int commentId)
219257
{
220-
var comment = service.GetById(id);
258+
var comment = service.GetById(commentId);
221259
if (comment == null)
222260
{
223261
return TypedResults.NotFound(new ResponseDTO<object> { Message = "Comment not found." });
224262
}
225263

226-
service.Delete(id);
264+
if (user.Role() == (int)Roles.student && comment.UserId != user.UserRealId())
265+
{
266+
var forbiddenResponse = new ResponseDTO<object>
267+
{
268+
Message = "You are not authorized to delete this comment."
269+
};
270+
return TypedResults.Json(forbiddenResponse, statusCode: StatusCodes.Status403Forbidden);
271+
}
272+
273+
service.Delete(commentId);
227274
service.Save();
228275

229276
return TypedResults.Ok(new ResponseDTO<object> { Message = "Comment deleted successfully." });
230277
}
231278

279+
[Authorize]
232280
[ProducesResponseType(StatusCodes.Status200OK)]
233281
[ProducesResponseType(StatusCodes.Status404NotFound)]
234282
private static IResult GetPostsByUser(IRepository<Post> service, IMapper mapper, int userid)
@@ -245,6 +293,7 @@ private static IResult GetPostsByUser(IRepository<Post> service, IMapper mapper,
245293
return TypedResults.Ok(response);
246294
}
247295

296+
[Authorize]
248297
[ProducesResponseType(StatusCodes.Status200OK)]
249298
[ProducesResponseType(StatusCodes.Status404NotFound)]
250299
private static IResult GetCommentsByUser(IRepository<PostComment> service, IMapper mapper, int userid)

exercise.wwwapi/Endpoints/UserEndpoints.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,16 @@ private static IResult Login(LoginRequestDTO request, IRepository<User> service,
103103

104104
// Check for valid password
105105
string validationResult = Validator.Password(request.password);
106+
Console.WriteLine($"Password: {validationResult}");
106107
if (validationResult != "Accepted") return TypedResults.BadRequest(new ResponseDTO<string>() { Message = "Invalid email and/or password provided" });
108+
107109

108110
//email doesn't exist, should probably be 404 user not found, but should maybe just say invalid email or password
109111
//check if email is in database
110112
var emailExists = service.GetAllFiltered(q => q.Email == request.email);
113+
Console.WriteLine($"Email doesnt exists? {emailExists.Count() == 0}");
111114
if (emailExists.Count() == 0) return TypedResults.BadRequest(new ResponseDTO<Object>() { Message = "Invalid email and/or password provided"});
112-
115+
113116

114117

115118
User user = service.GetAll().FirstOrDefault(u => u.Email == request.email)!;
@@ -118,6 +121,7 @@ private static IResult Login(LoginRequestDTO request, IRepository<User> service,
118121
if (!BCrypt.Net.BCrypt.Verify(request.password, user.PasswordHash))
119122
{
120123
// should probably be 401 unauthorized
124+
Console.WriteLine($"Password hashes doesnt match:\n{request.password}\n{user.PasswordHash}");
121125
return Results.BadRequest(new ResponseDTO<Object>() { Message = "Invalid email and/or password provided" });
122126
}
123127

@@ -227,8 +231,10 @@ private static string CreateToken(User user, IConfigurationSettings config)
227231
List<Claim> claims = new List<Claim>
228232
{
229233
new Claim(ClaimTypes.Sid, user.Id.ToString()),
234+
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
230235
new Claim(ClaimTypes.Name, user.Username),
231-
new Claim(ClaimTypes.Email, user.Email)
236+
new Claim(ClaimTypes.Email, user.Email),
237+
new Claim(ClaimTypes.Role, ((int)user.Role).ToString())
232238
};
233239

234240
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config.GetValue("AppSettings:Token")));

0 commit comments

Comments
 (0)