Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #588

Merged
merged 7 commits into from
Jan 8, 2025
Merged

Dev #588

Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"expo": {
"name": "TAMU SHPE",
"slug": "TAMU-SHPE",
"version": "1.0.22",
"version": "1.0.23",
"owner": "tamu-shpe",
"orientation": "portrait",
"icon": "./assets/icon.png",
Expand Down Expand Up @@ -54,7 +54,7 @@
"permissions": [
"android.permission.RECORD_AUDIO"
],
"versionCode": 105,
"versionCode": 110,
"userInterfaceStyle": "automatic",
"config": {
"googleMaps": {
Expand Down
8 changes: 4 additions & 4 deletions functions/src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export const eventSignIn = functions.https.onCall(async (data, context) => {

// Only allow privileged users to sign-in for other users
const token = context.auth.token;
if (uid !== context.auth.uid && (token.admin !== true && token.officer !== true && token.developer !== true && token.secretary !== true && token.lead !== true && token.representative !== true)) {
if (uid !== context.auth.uid && (token.admin !== true && token.officer !== true && token.developer !== true && token.secretary !== true && token.representative !== true)) {
functions.logger.warn(`${context.auth.token} attempted to sign in as ${uid} with invalid permissions.`);
throw new functions.https.HttpsError("permission-denied", `Invalid credentials`);
}
Expand Down Expand Up @@ -188,7 +188,7 @@ export const eventSignOut = functions.https.onCall(async (data, context) => {

// Only allow privileged users to sign-out for other users
const token = context.auth.token;
if (uid !== context.auth.uid && (token.admin !== true && token.officer !== true && token.developer !== true && token.secretary !== true && token.lead !== true && token.representative !== true)) {
if (uid !== context.auth.uid && (token.admin !== true && token.officer !== true && token.developer !== true && token.secretary !== true && token.representative !== true)) {
functions.logger.warn(`${context.auth.token} attempted to sign in as ${uid} with invalid permissions.`);
throw new functions.https.HttpsError("permission-denied", `Invalid credentials`);
}
Expand Down Expand Up @@ -261,7 +261,7 @@ export const addInstagramPoints = functions.https.onCall(async (data, context) =
}

const token = context.auth.token;
if (token.admin !== true && token.officer !== true && token.developer !== true && token.lead !== true && token.representative !== true) {
if (token.admin !== true && token.officer !== true && token.developer !== true && token.representative !== true) {
throw new functions.https.HttpsError("permission-denied", `Invalid credentials`);
}

Expand Down Expand Up @@ -313,7 +313,7 @@ export const eventLogDelete = functions.https.onCall(async (data, context) => {

// Only allow privileged users to sign-out for other users
const token = context.auth.token;
if (uid !== context.auth.uid && (token.admin !== true && token.officer !== true && token.developer !== true && token.secretary !== true && token.lead !== true && token.representative !== true)) {
if (uid !== context.auth.uid && (token.admin !== true && token.officer !== true && token.developer !== true && token.secretary !== true && token.representative !== true)) {
functions.logger.warn(`${context.auth.token} attempted to sign in as ${uid} with invalid permissions.`);
throw new functions.https.HttpsError("permission-denied", `Invalid credentials`);
}
Expand Down
4 changes: 2 additions & 2 deletions functions/src/pointSheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export const updateUserPoints = functions.https.onCall(async (data, context) =>
}

const token = context.auth.token;
if (token.admin !== true && token.officer !== true && token.developer !== true && token.secretary !== true && token.lead !== true && token.representative !== true) {
if (token.admin !== true && token.officer !== true && token.developer !== true && token.secretary !== true && token.representative !== true) {
throw new functions.https.HttpsError("permission-denied", `Invalid credentials`);
}

Expand All @@ -138,7 +138,7 @@ export const updateAllUserPoints = functions.https.onCall(async (_, context) =>
}

const token = context.auth.token;
if (token.admin !== true && token.officer !== true && token.developer !== true && token.secretary !== true && token.lead !== true && token.representative !== true) {
if (token.admin !== true && token.officer !== true && token.developer !== true && token.secretary !== true && token.representative !== true) {
throw new functions.https.HttpsError("permission-denied", `Invalid credentials`);
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "shpe-app",
"version": "1.0.22",
"version": "1.0.23",
"scripts": {
"start": "npx expo start --dev-client",
"test": "jest --coverage=true --verbose --bail --config ./jest.config.ts",
Expand Down
5 changes: 3 additions & 2 deletions src/components/MOTMCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useFocusEffect } from '@react-navigation/core';
import { UserContext } from '../context/UserContext';
import { truncateStringWithEllipsis } from '../helpers/stringUtils';
import { auth } from '../config/firebaseConfig';
import { hasPrivileges } from '../helpers/rolesUtils';

const MOTMCard: React.FC<MemberCardProp> = ({ navigation }) => {
const userContext = useContext(UserContext);
Expand All @@ -18,7 +19,7 @@ const MOTMCard: React.FC<MemberCardProp> = ({ navigation }) => {
const colorScheme = useColorScheme();
const darkMode = useSystemDefault ? colorScheme === 'dark' : fixDarkMode;

const hasPrivileges = (userInfo?.publicInfo?.roles?.admin?.valueOf() || userInfo?.publicInfo?.roles?.officer?.valueOf() || userInfo?.publicInfo?.roles?.developer?.valueOf() || userInfo?.publicInfo?.roles?.lead?.valueOf() || userInfo?.publicInfo?.roles?.representative?.valueOf());
const isAdmin = hasPrivileges(userInfo!, ['admin', 'officer', 'developer', 'representative']);

const [MOTM, setMOTM] = useState<PublicUserInfo>();
const [currentUser, setCurrentUser] = useState(auth.currentUser);
Expand Down Expand Up @@ -78,7 +79,7 @@ const MOTMCard: React.FC<MemberCardProp> = ({ navigation }) => {
return;
}

if (hasPrivileges) {
if (isAdmin) {
fetchMOTM();
}
}, [currentUser])
Expand Down
32 changes: 16 additions & 16 deletions src/config/firebaseConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,21 @@ const db = getFirestore(app);
const storage = getStorage(app);
const functions = getFunctions(app);

if (process.env.FIREBASE_EMULATOR_ADDRESS !== undefined) {
const address = process.env.FIREBASE_EMULATOR_ADDRESS;
console.debug("Connecting to firebase emulators.");
if (process.env.FIREBASE_AUTH_PORT !== undefined) {
connectAuthEmulator(auth, `http://${address}:${process.env.FIREBASE_AUTH_PORT}`);
}
if (process.env.FIREBASE_FIRESTORE_PORT !== undefined) {
connectFirestoreEmulator(db, address, Number(process.env.FIREBASE_FIRESTORE_PORT));
}
if (process.env.FIREBASE_CLOUD_FUNCTIONS_PORT !== undefined) {
connectFunctionsEmulator(functions, address, Number(process.env.FIREBASE_CLOUD_FUNCTIONS_PORT));
}
if (process.env.FIREBASE_STORAGE_PORT !== undefined) {
connectStorageEmulator(storage, address, Number(process.env.FIREBASE_STORAGE_PORT));
}
}
// if (process.env.FIREBASE_EMULATOR_ADDRESS !== undefined) {
// const address = process.env.FIREBASE_EMULATOR_ADDRESS;
// console.debug("Connecting to firebase emulators.");
// if (process.env.FIREBASE_AUTH_PORT !== undefined) {
// connectAuthEmulator(auth, `http://${address}:${process.env.FIREBASE_AUTH_PORT}`);
// }
// if (process.env.FIREBASE_FIRESTORE_PORT !== undefined) {
// connectFirestoreEmulator(db, address, Number(process.env.FIREBASE_FIRESTORE_PORT));
// }
// if (process.env.FIREBASE_CLOUD_FUNCTIONS_PORT !== undefined) {
// connectFunctionsEmulator(functions, address, Number(process.env.FIREBASE_CLOUD_FUNCTIONS_PORT));
// }
// if (process.env.FIREBASE_STORAGE_PORT !== undefined) {
// connectStorageEmulator(storage, address, Number(process.env.FIREBASE_STORAGE_PORT));
// }
// }

export { db, auth, storage, functions };
23 changes: 23 additions & 0 deletions src/helpers/rolesUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Roles, User } from "../types/user";

/**
* Returns a boolean stating whether or not a user object has the given roles.
*
* Note that these are not the same as *claims* and should only be used for frontend things like
* accessing certain screens or showing certain buttons.
*
* @param userInfo The user information object.
* @param roles The roles to check for the given user.
* @returns {boolean} Whether or not the given `User` object has the specified roles.
*
* @example
* const isSuperuser = hasPrivileges(user, ['admin', 'developer']);
*/
export const hasPrivileges = (
userInfo: User,
roles: (keyof Roles)[] = ['admin', 'officer', 'developer', 'lead', 'representative']
): boolean => {
const userRoles = userInfo?.publicInfo?.roles || {};
return roles.some(role => userRoles[role]?.valueOf());
};

7 changes: 4 additions & 3 deletions src/screens/committees/CommitteeInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { PublicUserInfo } from '../../types/user';
import { CommitteesStackParams } from '../../types/navigation';
import MembersList from '../../components/MembersList';
import EventCard from '../events/EventCard';
import { hasPrivileges } from '../../helpers/rolesUtils';

const CommitteeInfo: React.FC<CommitteeInfoScreenRouteProps> = ({ route, navigation }) => {
const initialCommittee = route.params.committee;
Expand All @@ -34,7 +35,7 @@ const CommitteeInfo: React.FC<CommitteeInfoScreenRouteProps> = ({ route, navigat
const colorScheme = useColorScheme();
const darkMode = useSystemDefault ? colorScheme === 'dark' : fixDarkMode;

const hasPrivileges = (userInfo?.publicInfo?.roles?.admin?.valueOf() || userInfo?.publicInfo?.roles?.officer?.valueOf() || userInfo?.publicInfo?.roles?.developer?.valueOf() || userInfo?.publicInfo?.roles?.lead?.valueOf() || userInfo?.publicInfo?.roles?.representative?.valueOf());
const isAdminLead = hasPrivileges(userInfo!, ['admin', 'officer', 'developer', 'representative', 'lead']);

const [events, setEvents] = useState<SHPEEvent[]>([]);
const [members, setMembers] = useState<PublicUserInfo[]>([]);
Expand Down Expand Up @@ -128,7 +129,7 @@ const CommitteeInfo: React.FC<CommitteeInfoScreenRouteProps> = ({ route, navigat

useFocusEffect(
useCallback(() => {
if (hasPrivileges) {
if (isAdminLead) {
fetchTeamMemberData();
}
}, [fetchTeamMemberData])
Expand Down Expand Up @@ -208,7 +209,7 @@ const CommitteeInfo: React.FC<CommitteeInfoScreenRouteProps> = ({ route, navigat
<TouchableOpacity onPress={() => navigation.goBack()} className='py-2 px-4'>
<Octicons name="chevron-left" size={30} color={darkMode ? "white" : "black"} />
</TouchableOpacity>
{hasPrivileges && (
{isAdminLead && (
<TouchableOpacity
onPress={() => { navigation.navigate("CommitteeEditor", { committee: initialCommittee }) }}
className='items-center justify-center px-4 py-1'
Expand Down
9 changes: 5 additions & 4 deletions src/screens/committees/Committees.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { SafeAreaView } from 'react-native-safe-area-context';
import { CommitteesStackParams } from '../../types/navigation';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { StatusBar } from 'expo-status-bar';
import { hasPrivileges } from '../../helpers/rolesUtils';

const Committees = ({ navigation }: NativeStackScreenProps<CommitteesStackParams>) => {
const userContext = useContext(UserContext);
Expand All @@ -22,7 +23,7 @@ const Committees = ({ navigation }: NativeStackScreenProps<CommitteesStackParams
const colorScheme = useColorScheme();
const darkMode = useSystemDefault ? colorScheme === 'dark' : fixDarkMode;

const hasPrivileges = (userInfo?.publicInfo?.roles?.admin?.valueOf() || userInfo?.publicInfo?.roles?.officer?.valueOf() || userInfo?.publicInfo?.roles?.developer?.valueOf() || userInfo?.publicInfo?.roles?.lead?.valueOf() || userInfo?.publicInfo?.roles?.representative?.valueOf());
const isAdminLead = hasPrivileges(userInfo!, ['admin', 'officer', 'developer', 'representative', 'lead']);

const [committees, setCommittees] = useState<Committee[]>([]);
const [filteredCommittees, setFilteredCommittees] = useState<Committee[]>([]);
Expand Down Expand Up @@ -79,10 +80,10 @@ const Committees = ({ navigation }: NativeStackScreenProps<CommitteesStackParams

useFocusEffect(
useCallback(() => {
if (hasPrivileges) {
if (isAdminLead) {
fetchCommittees();
}
}, [hasPrivileges])
}, [isAdminLead])
);

return (
Expand Down Expand Up @@ -151,7 +152,7 @@ const Committees = ({ navigation }: NativeStackScreenProps<CommitteesStackParams
</ScrollView>

{/* Create Committee */}
{hasPrivileges && (
{isAdminLead && (
<TouchableOpacity
className='absolute bottom-0 right-0 bg-primary-blue rounded-full h-14 w-14 shadow-lg justify-center items-center m-4'
style={{
Expand Down
6 changes: 4 additions & 2 deletions src/screens/events/EventCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Images } from '../../../assets'
import { formatDate } from '../../helpers/timeUtils'
import { truncateStringWithEllipsis } from '../../helpers/stringUtils';
import { SHPEEvent } from '../../types/events'
import { hasPrivileges } from '../../helpers/rolesUtils';

const EventCard = ({ event, navigation }: { event: SHPEEvent, navigation: any }) => {
const userContext = useContext(UserContext);
Expand All @@ -15,7 +16,8 @@ const EventCard = ({ event, navigation }: { event: SHPEEvent, navigation: any })
const colorScheme = useColorScheme();
const darkMode = useSystemDefault ? colorScheme === 'dark' : fixDarkMode;

const hasPrivileges = (userInfo?.publicInfo?.roles?.admin?.valueOf() || userInfo?.publicInfo?.roles?.officer?.valueOf() || userInfo?.publicInfo?.roles?.developer?.valueOf() || userInfo?.publicInfo?.roles?.lead?.valueOf() || userInfo?.publicInfo?.roles?.representative?.valueOf());
const isAdminLead = hasPrivileges(userInfo!, ['admin', 'officer', 'developer', 'representative', 'lead']);
const isCoach = hasPrivileges(userInfo!, ['coach']);

return (
<TouchableOpacity
Expand Down Expand Up @@ -54,7 +56,7 @@ const EventCard = ({ event, navigation }: { event: SHPEEvent, navigation: any })
<Text className={`text-md font-semibold ${darkMode ? "text-white" : "text-black"}`}>{formatDate(event.startTime?.toDate()!)}</Text>
</View>

{hasPrivileges && (
{(isAdminLead || isCoach) && (
<View className='h-full items-center justify-center mx-2'>
<TouchableOpacity
onPress={() => { navigation.navigate("QRCode", { event: event }) }}
Expand Down
Loading
Loading