Skip to content
Open
Show file tree
Hide file tree
Changes from 68 commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
bb9e5ab
initial commit - move the code from the old branch to new branch in o…
ArabellaJi May 23, 2024
fbcb629
correct typos and keep consistency
ArabellaJi Jun 4, 2024
a937cfd
got the drop down menus Public/FacStaff/Private to work again
tsion-tez Jun 4, 2024
d706e46
Revert "got the drop down menus Public/FacStaff/Private to work again"
Jun 7, 2024
275360c
Merge branch 'develop' into s24-privacy
jsenning Jun 11, 2024
0b7898f
update IsMobilePhonePrivate
tsion-tez Jun 18, 2024
1976b02
Lay grond work for imposing privacy constraints after composing profile
jsenning Jun 25, 2024
34f5edd
Continued working on ImposePrivacySettings()
jsenning Jun 26, 2024
05e92a3
in progress: some profile entries are not objects
jsenning Jun 28, 2024
b5140c4
Merge branch 'develop' into s24-privacy-combined-profile
jsenning Jun 28, 2024
962061b
Changed ProfileItem; Encapsulated some profile information
jsenning Jun 28, 2024
9f9dd59
Converted names to ProfileItems
jsenning Jun 30, 2024
eeb400a
a bit of cleanup
jsenning Jun 30, 2024
4ca5319
removed homephone from student public view
tsion-tez Jul 1, 2024
3b3356a
draft: refactor visibility for reuse
russtuck Jul 1, 2024
649514c
Removed HomeFax from ViewModels; several new ProfileItems
jsenning Jul 2, 2024
0e8fcfb
refactored ImposePrivacySettings
jsenning Jul 2, 2024
cbc1c94
Untested refactor with no intentional change in function
russtuck Jul 2, 2024
7677b39
Add rebuilt documentation
russtuck Jul 2, 2024
6ba3af5
Merge branch 's24-privacy-combined-profile' into s24-privacy-combined…
russtuck Jul 2, 2024
f79863d
snapshot: testing, partly fixed, not working
russtuck Jul 8, 2024
3040ee2
Clean up variable names and magic values
jsenning Jul 8, 2024
549cf8b
Merge branch 's24-privacy-combined-profile' into s24-privacy-combined…
russtuck Jul 8, 2024
900c470
Merge branch 'develop' into s24-privacy-combined-profile
jsenning Jul 9, 2024
2e70441
Add CanISeeThisStudent to help enforce FERPA visibility limits
russtuck Jul 9, 2024
029bd6b
Delete obsolete code and make 'buildings' same as 'building'
russtuck Jul 10, 2024
1710440
removed old (and commented out) code
jsenning Jul 10, 2024
13d0d6a
Move 'CanISee...' and 'VisibleToMe...' to more logical home
russtuck Jul 10, 2024
df19ee1
Use 'state your business' to control access to privacy setting
jsenning Jul 11, 2024
6508b80
Update Gordon360/Static Classes/Names.cs
jsenning Jul 12, 2024
c64c78d
Update Gordon360/Services/ProfileService.cs
jsenning Jul 12, 2024
eac8dae
Update Gordon360/Services/ProfileService.cs
jsenning Jul 12, 2024
479b55b
Merge branch 'develop' into s24-privacy-combined-profile
jsenning Jul 12, 2024
3851170
Merge branch 'develop' into s24-privacy-combined-profile-state-your-b…
jsenning Jul 12, 2024
a243aef
cleanup and added comments
jsenning Jul 12, 2024
7de58f7
Merged s24-privacy-combined-profile-state-your-business
jsenning Jul 12, 2024
b46292c
Fix security hole that made all schedules public
russtuck Jul 12, 2024
470cafc
Updated a couple of comments
jsenning Jul 12, 2024
1cbe544
KeepPrivate for FERPA: Y and P
russtuck Jul 15, 2024
83a96c8
Delete obsolete TODO comments
russtuck Jul 15, 2024
ec9b181
Merge branch 'develop' into s24-privacy-combined-profile
jsenning May 27, 2025
0349a4b
Merge branch 'develop' into s24-privacy-combined-profile
jsenning Jun 9, 2025
0a9d849
Merged develop into s24-privacy-combined-profile-ferpa
jsenning Jun 12, 2025
9115002
XML documentation auto-updated
jsenning Jun 12, 2025
54ca66f
add code that should have been kept when merged with develop
jsenning Jun 12, 2025
51dc364
Update Gordon360/Models/ViewModels/UserPrivacyUpdateViewModel.cs
jsenning Jun 12, 2025
caf9029
Increased degrees of control in user privacy settings
jsenning Jun 13, 2025
3bd3f95
merged s24-privacy-combined-profile-ferpa into s25-privacy-combined-p…
jsenning Jun 16, 2025
d69c8b5
Merged develop into branch
jsenning Jun 16, 2025
3f3474a
Added FirstHireDt and Entrance_Date to profiles
jsenning Jun 16, 2025
f582d1a
Merge branch 'develop' into s24-privacy-combined-profile
jsenning Jun 16, 2025
f2b3068
Enforce home city privacy during advanced search
jsenning Jun 18, 2025
e6da0ac
update comments and clarify route and method names
jsenning Jun 25, 2025
e3a1f56
merged with develop
jsenning Jun 25, 2025
d212ff7
clean up
jsenning Jun 27, 2025
df652c0
Merge branch 'develop' into s24-privacy-combined-profile
jsenning Jun 30, 2025
9443930
fixed bug introduced during merge with develop
jsenning Jun 30, 2025
71333e0
Whoops - forgot to include xml doc file
jsenning Jun 30, 2025
693f8c3
updated comments
jsenning Jun 30, 2025
1875011
Updated comments on UserPrivacy view models
jsenning Jun 30, 2025
2636a83
in-progress: handling default privacy settings
jsenning Jul 1, 2025
c1bcf9f
Use old-style profile privacy settings if privacy data has not been set
jsenning Jul 2, 2025
6c435b4
Merge branch 'develop' into s24-privacy-combined-profile
jsenning Jul 3, 2025
e71d709
Distinguish courses taught from courses taken
russtuck Jul 7, 2025
f057f43
Revert "Distinguish courses taught from courses taken"
russtuck Jul 7, 2025
6d693ec
Distinguish courses taught from courses taken (2nd try)
russtuck Jul 7, 2025
f96d73a
Fix type error
russtuck Jul 7, 2025
4bee957
Merge from develop
russtuck Jul 7, 2025
318572f
Update Gordon360/Controllers/ProfilesController.cs
jsenning Jul 7, 2025
82ec320
Update Gordon360/Controllers/ProfilesController.cs
jsenning Jul 7, 2025
0ab2c3f
Update Gordon360/Services/ProfileService.cs
jsenning Jul 7, 2025
f978185
Fix access to own schedule and alumni schedules
russtuck Jul 8, 2025
1b7c402
Remove redundant and problematic StateYourBusiness; update docs
russtuck Jul 8, 2025
552528c
Converted ProfileItem to use generics
jsenning Jul 9, 2025
e52d782
Removed PublicFacultyStaffProfileViewModel and PublicAlumniProfileVie…
jsenning Jul 10, 2025
78688de
Merge branch 'develop' into s24-privacy-combined-profile
jsenning Jul 11, 2025
09fe1fa
Merge branch 'develop' into s24-privacy-combined-profile
jsenning Jul 11, 2025
dfadb69
Use int Ids as keys for user privacy
EjPlatzer Jul 14, 2025
67c7e95
Merge branch 's24-privacy-combined-profile' of https://github.com/gor…
EjPlatzer Jul 14, 2025
b73e832
Merge branch 's24-privacy-combined-profile' of github.com:gordon-cs/g…
jsenning Jul 14, 2025
4fa0bba
Merge branch 'develop' into s24-privacy-combined-profile
jsenning Jul 14, 2025
8d720b9
unfinished changes necessitated by DB change
jsenning Jul 15, 2025
2825306
in-progress: API compiles but ViewModels not yet correct
jsenning Jul 16, 2025
071b100
Working API
jsenning Jul 16, 2025
9fb875b
Merge branch 'develop' into s24-privacy-combined-profile
jsenning Jul 16, 2025
f57a12d
Receive privacy field and visibility as strings from UI
jsenning Jul 16, 2025
f52c9a1
Updated Schedule Controller test
jsenning Jul 16, 2025
1008f36
Enforce semi-private restriction for students
jsenning Jul 16, 2025
aa5803a
Merge branch 'develop' into s24-privacy-combined-profile
jsenning Jul 18, 2025
4e41543
Fix merge develop into s24-privacy-combined-profile
jsenning Jul 18, 2025
b8c7299
Merge branch 'develop' into s24-privacy-combined-profile
jsenning Jul 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Gordon360/Authorization/StateYourBusiness.cs
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,16 @@ private async Task<bool> CanUpdateAsync(string resource)

return false;
}
case Resource.PROFILE_PRIVACY:
{
// Currently all users can change their privacy settings
// in their profile. To limit this functionality to
// certian user types, change the return statement to be
// something like:
// return user_groups.Contains(AuthGroup.FacStaff);

return true;
}
case Resource.ACTIVITY_INFO:
{
// User is admin
Expand Down
17 changes: 1 addition & 16 deletions Gordon360/Controllers/AdvancedSearchController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,28 +125,13 @@ public ActionResult<IEnumerable<string>> GetDepartments()
/// <returns> All buildings</returns>
[HttpGet]
[Route("building")]
[Route("buildings")]
public async Task<ActionResult<IEnumerable<BuildingViewModel>>> GetBuildingsAsync([FromServices] webSQLContext webSQLContext)
{
var buildings = await webSQLContext.Procedures.account_list_buildingsAsync();
return Ok(buildings.Select(b => new BuildingViewModel(b.BLDG_CDE, b.BUILDING_DESC)));
}

/// <summary>
/// Return a list of buildings.
/// </summary>
/// <returns> All buildings</returns>
[Obsolete("Use GetBuildingsAsync that gives structured building data")]
[HttpGet]
[Route("buildings")]
public ActionResult<IEnumerable<string>> GetBuildings()
{
var buildings = context.FacStaff.Select(fs => fs.BuildingDescription)
.Distinct()
.Where(d => d != null)
.OrderBy(d => d);
return Ok(buildings);
}

/// <summary>
/// Return a list of involvements' descriptions.
/// </summary>
Expand Down
105 changes: 61 additions & 44 deletions Gordon360/Controllers/ProfilesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Gordon360.Models.CCT.Context;

namespace Gordon360.Controllers;

[Route("api/[controller]")]
public class ProfilesController(IProfileService profileService,
IAccountService accountService,
IMembershipService membershipService,
IConfiguration config) : GordonControllerBase
IConfiguration config,
CCTContext context) : GordonControllerBase
{

/// <summary>Get profile info of currently logged in user</summary>
Expand All @@ -44,66 +46,38 @@ public class ProfilesController(IProfileService profileService,
return Ok(null);
}

var profile = profileService.ComposeProfile(student, alumni, faculty, customInfo);
//var profile = profileService.ComposeProfile(student, alumni, faculty, customInfo);
var profile = (CombinedProfileViewModel) profileService.ComposeProfile(student, alumni, faculty, customInfo);

return Ok(profile);
}

/// <summary>Get public profile info for a user</summary>
/// <summary>Get another user's profile info. The info returned depends
/// on the permissions of the current users, who is making the request.</summary>
/// <param name="username">username of the profile info</param>
/// <returns></returns>
[HttpGet]
[Route("{username}")]
public ActionResult<ProfileViewModel?> GetUserProfile(string username)
public ActionResult<ProfileViewModel?> GetUserProfileAsync(string username)
{
var viewerGroups = AuthUtils.GetGroups(User);

var _student = profileService.GetStudentProfileByUsername(username);
var _faculty = profileService.GetFacultyStaffProfileByUsername(username);
var _alumni = profileService.GetAlumniProfileByUsername(username);
StudentProfileViewModel? _student = profileService.GetStudentProfileByUsername(username);
FacultyStaffProfileViewModel? _facstaff = profileService.GetFacultyStaffProfileByUsername(username);
AlumniProfileViewModel? _alumni = profileService.GetAlumniProfileByUsername(username);
var _customInfo = profileService.GetCustomUserInfo(username);

object? student = null;
object? faculty = null;
object? alumni = null;
var student = accountService.VisibleToMeStudent(viewerGroups, _student);
var facstaff = accountService.VisibleToMeFacstaff(viewerGroups, _facstaff);
var alumni = accountService.VisibleToMeAlumni(viewerGroups, _alumni);

if (viewerGroups.Contains(AuthGroup.SiteAdmin) || viewerGroups.Contains(AuthGroup.Police))
{
student = _student;
faculty = _faculty;
alumni = _alumni;
}
else if (viewerGroups.Contains(AuthGroup.FacStaff))
{
student = _student;
faculty = _faculty == null ? null : (PublicFacultyStaffProfileViewModel)_faculty;
alumni = _alumni == null ? null : (PublicAlumniProfileViewModel)_alumni;
}
else if (viewerGroups.Contains(AuthGroup.Student))
{
student = _student == null ? null : (PublicStudentProfileViewModel)_student;
faculty = _faculty == null ? null : (PublicFacultyStaffProfileViewModel)_faculty;
// If this student is also in Alumni AuthGroup, then s/he can see alumni's
// public profile; if not, return null.
alumni = (_alumni == null) ? null :
viewerGroups.Contains(AuthGroup.Alumni) ?
(PublicAlumniProfileViewModel)_alumni : null;
}
else if (viewerGroups.Contains(AuthGroup.Alumni))
{
student = null;
faculty = _faculty == null ? null : (PublicFacultyStaffProfileViewModel)_faculty;
alumni = _alumni == null ? null : (PublicAlumniProfileViewModel)_alumni;
}

if (student is null && alumni is null && faculty is null)
if (student is null && alumni is null && facstaff is null)
{
return Ok(null);
}

var profile = profileService.ComposeProfile(student, alumni, faculty, _customInfo);

return Ok(profile);
var profile = profileService.ComposeProfile(student, alumni, facstaff, _customInfo);
var visible_profile = profileService.ImposePrivacySettings(viewerGroups, profile);
return Ok(visible_profile);
}

///<summary>Get the advisor(s) of a particular student</summary>
Expand All @@ -121,6 +95,20 @@ public async Task<ActionResult<IEnumerable<AdvisorViewModel>>> GetAdvisorsAsync(
return Ok(advisors);
}

///<summary>Get the privacy settings of a particular user</summary>
/// <returns>
/// All privacy settings of the given user.
/// </returns>
[HttpGet]
[Route("{username}/privacy_settings")]
[StateYourBusiness(operation = Operation.READ_ONE, resource = Resource.PROFILE)]
public ActionResult<IEnumerable<UserPrivacyViewModel>> GetPrivacySettingsAsync(string username)
{
var privacy = profileService.GetPrivacySettingsAsync(username);

return Ok(privacy);
}

/// <summary> Gets the clifton strengths of a particular user </summary>
/// <param name="username"> The username for which to retrieve info </param>
/// <returns> Clifton strengths of the given user. </returns>
Expand Down Expand Up @@ -466,6 +454,35 @@ public async Task<ActionResult<FacultyStaffProfileViewModel>> UpdateOfficeHours(
return Ok(result);
}

/// <summary>
/// Set visibility group for some piece of personal data
/// </summary>
/// <param name="userPrivacy">Faculty Staff Privacy Decisions (see UserPrivacyUpdateViewModel)</param>
/// <returns></returns>
[HttpPut]
[Route("user_privacy")]
[StateYourBusiness(operation = Operation.UPDATE, resource = Resource.PROFILE_PRIVACY)]
public async Task<ActionResult<UserPrivacyUpdateViewModel>> UpdateUserPrivacyAsync(UserPrivacyUpdateViewModel userPrivacy)
{
var authenticatedUserUsername = AuthUtils.GetUsername(User);
await profileService.UpdateUserPrivacyAsync(authenticatedUserUsername, userPrivacy);
return Ok();
}

/// <summary>
/// Return a list visibility groups
/// </summary>
/// <returns> All visibility groups (Public, FacStaff, Private)</returns>
[HttpGet]
[Route("visibility_groups")]
public ActionResult<IEnumerable<string>> GetVisibilityGroup()
{
var groups = context.UserPrivacy_Visibility_Groups.Select(up_v_g => up_v_g.Group)
.Distinct()
.Where(g => g != null);
return Ok(groups);
}

/// <summary>
/// Update mail location
/// </summary>
Expand Down
52 changes: 34 additions & 18 deletions Gordon360/Controllers/ScheduleController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
namespace Gordon360.Controllers;

[Route("api/[controller]")]
public class ScheduleController(IScheduleService scheduleService) : GordonControllerBase
public class ScheduleController(IProfileService profileService,
IScheduleService scheduleService,
IAccountService accountService) : GordonControllerBase
{
/// <summary>
/// Gets all session objects for a user
Expand All @@ -24,22 +26,30 @@ public class ScheduleController(IScheduleService scheduleService) : GordonContro
public async Task<ActionResult<CoursesBySessionViewModel>> GetAllCourses(string username)
{
var groups = AuthUtils.GetGroups(User);
var authenticatedUsername = AuthUtils.GetUsername(User);
FacultyStaffProfileViewModel? fac = profileService.GetFacultyStaffProfileByUsername(username);
StudentProfileViewModel? student = profileService.GetStudentProfileByUsername(username);
AlumniProfileViewModel? alumni = profileService.GetAlumniProfileByUsername(username);

IEnumerable<CoursesBySessionViewModel> result;
if (authenticatedUsername.EqualsIgnoreCase(username) || groups.Contains(AuthGroup.FacStaff))
// Some users can see schedules of courses taken, as well as taught,
// so check to see if this user can see all courses for this person.
if ((accountService.CanISeeStudentSchedule(groups) &&
student != null &&
accountService.CanISeeThisStudent(groups, student)) ||
(alumni != null && accountService.CanISeeAlumni(groups)))
{
result = await scheduleService.GetAllCoursesAsync(username);
IEnumerable<CoursesBySessionViewModel> result = await scheduleService.GetAllCoursesAsync(username);
return Ok(result);
}
else
{
result = await scheduleService.GetAllInstructorCoursesAsync(username);
// Everyone can see schedules of courses taught.
IEnumerable<CoursesBySessionViewModel> result = await scheduleService.GetAllInstructorCoursesAsync(username);
return Ok(result);
}
return Ok(result);
}

/// <summary>
/// Gets all term objects for a user
/// Gets all visible course objects for a user, for all visible terms
/// </summary>
/// <returns>A IEnumerable of term objects as well as the schedules</returns>
[HttpGet]
Expand All @@ -48,19 +58,25 @@ public async Task<ActionResult<CoursesBySessionViewModel>> GetAllCourses(string
public async Task<ActionResult<IEnumerable<CoursesByTermViewModel>>> GetAllCoursesByTerm(string username)
{
var groups = AuthUtils.GetGroups(User);
var authenticatedUsername = AuthUtils.GetUsername(User);
FacultyStaffProfileViewModel? fac = profileService.GetFacultyStaffProfileByUsername(username);
StudentProfileViewModel? student = profileService.GetStudentProfileByUsername(username);
AlumniProfileViewModel? alumni = profileService.GetAlumniProfileByUsername(username);

IEnumerable<CoursesByTermViewModel> result;
if (authenticatedUsername.EqualsIgnoreCase(username) || groups.Contains(AuthGroup.FacStaff))
// Some users can see schedules of courses taken, as well as taught,
// so check to see if this user can see all courses for this person.
if ((accountService.CanISeeStudentSchedule(groups) &&
student != null &&
accountService.CanISeeThisStudent(groups, student)) ||
(alumni != null && accountService.CanISeeAlumni(groups)))
{
result = await scheduleService.GetAllCoursesByTermAsync(username);
}
else
IEnumerable<CoursesByTermViewModel> result = await scheduleService.GetAllCoursesByTermAsync(username);
return Ok(result);
} else
{
result = await scheduleService.GetAllInstructorCoursesByTermAsync(username);
// Everyone can see schedules of courses taught.
IEnumerable<CoursesByTermViewModel> result = await scheduleService.GetAllInstructorCoursesByTermAsync(username);
return Ok(result);
}

return Ok(result);
}

/// <summary>
Expand All @@ -72,6 +88,6 @@ public async Task<ActionResult<IEnumerable<CoursesByTermViewModel>>> GetAllCours
public async Task<ActionResult<bool>> GetCanReadStudentSchedules()
{
var groups = AuthUtils.GetGroups(User);
return groups.Contains(AuthGroup.Advisors);
return accountService.CanISeeStudentSchedule(groups);
}
}
Loading
Loading