feat: add Google Workspace SSO configuration per school
All checks were successful
Build & Push / Build & Push image (push) Successful in 39s

- Implemented Google SSO management in the school settings, allowing schools to configure their own OAuth2 credentials.
- Added fields for Client ID and Client Secret in the edit school modal and school detail page.
- Introduced functionality to save and clear Google SSO settings via API.
- Updated UI to display current SSO status and instructions for setting up Google OAuth2.
- Created a new database migration to add `google_client_id` and `google_client_secret` columns to the schools table.
This commit is contained in:
2026-03-03 22:40:14 +01:00
parent 55cd055645
commit b470cd017e
8 changed files with 607 additions and 343 deletions

View File

@@ -102,6 +102,55 @@ def update_school(school_id):
return jsonify({'school': school.to_dict()})
@admin_bp.route('/schools/<int:school_id>/google-sso', methods=['PUT'])
@login_required
@school_ict_required
def update_school_google_sso(school_id):
"""
Sla Google Workspace OAuth2 credentials op voor een school.
Toegankelijk voor scholengroep_ict (alle scholen) én school_ict
(enkel hun eigen school).
Body:
google_client_id: string (verplicht om in te stellen)
google_client_secret: string (verplicht om in te stellen)
clear: boolean (optioneel — verwijdert de credentials)
"""
# School ICT mag enkel zijn eigen school aanpassen
if not current_user.is_scholengroep_ict and current_user.school_id != school_id:
return jsonify({'error': 'Geen toegang tot deze school'}), 403
school = School.query.get_or_404(school_id)
data = request.get_json() or {}
if data.get('clear'):
school.google_client_id = None
school.google_client_secret = None
audit_log('school.google_sso_removed', 'school',
target_type='school', target_id=str(school_id),
detail={'name': school.name}, school_id=school_id)
db.session.commit()
return jsonify({'school': school.to_dict(), 'message': 'Google SSO verwijderd'})
client_id = (data.get('google_client_id') or '').strip()
client_secret = (data.get('google_client_secret') or '').strip()
if not client_id or not client_secret:
return jsonify({'error': 'Zowel Client ID als Client Secret zijn verplicht'}), 400
# Basis validatie: Google client IDs eindigen op .apps.googleusercontent.com
if not client_id.endswith('.apps.googleusercontent.com'):
return jsonify({'error': 'Ongeldig Client ID — moet eindigen op .apps.googleusercontent.com'}), 400
school.google_client_id = client_id
school.google_client_secret = client_secret
audit_log('school.google_sso_configured', 'school',
target_type='school', target_id=str(school_id),
detail={'name': school.name}, school_id=school_id)
db.session.commit()
return jsonify({'school': school.to_dict(), 'message': 'Google SSO ingesteld'})
@admin_bp.route('/schools/<int:school_id>', methods=['DELETE'])
@login_required
@scholengroep_ict_required