Skip to content

Commit e66ee4b

Browse files
Fixes
1 parent 043d3aa commit e66ee4b

File tree

2 files changed

+99
-9
lines changed

2 files changed

+99
-9
lines changed

app/__init__.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,76 @@ def migrate_database():
194194
except Exception as e:
195195
print(f"⚠️ Could not create oidc_settings table (may already exist): {e}")
196196

197+
# Migration 5: Add manual endpoint columns to oidc_settings if they don't exist
198+
if inspector.has_table('oidc_settings'):
199+
oidc_columns = [col['name'] for col in inspector.get_columns('oidc_settings')]
200+
201+
if 'authorization_endpoint' not in oidc_columns:
202+
try:
203+
if db_dialect == 'postgresql':
204+
alter_sql = 'ALTER TABLE oidc_settings ADD COLUMN authorization_endpoint VARCHAR(500)'
205+
elif db_dialect == 'mysql':
206+
alter_sql = 'ALTER TABLE oidc_settings ADD COLUMN authorization_endpoint VARCHAR(500)'
207+
else: # SQLite
208+
alter_sql = 'ALTER TABLE oidc_settings ADD COLUMN authorization_endpoint VARCHAR(500)'
209+
210+
with db.engine.connect() as conn:
211+
conn.execute(text(alter_sql))
212+
conn.commit()
213+
migrations_applied.append("Added authorization_endpoint column to oidc_settings")
214+
except Exception as e:
215+
print(f"⚠️ Could not add authorization_endpoint column: {e}")
216+
217+
if 'token_endpoint' not in oidc_columns:
218+
try:
219+
if db_dialect == 'postgresql':
220+
alter_sql = 'ALTER TABLE oidc_settings ADD COLUMN token_endpoint VARCHAR(500)'
221+
elif db_dialect == 'mysql':
222+
alter_sql = 'ALTER TABLE oidc_settings ADD COLUMN token_endpoint VARCHAR(500)'
223+
else: # SQLite
224+
alter_sql = 'ALTER TABLE oidc_settings ADD COLUMN token_endpoint VARCHAR(500)'
225+
226+
with db.engine.connect() as conn:
227+
conn.execute(text(alter_sql))
228+
conn.commit()
229+
migrations_applied.append("Added token_endpoint column to oidc_settings")
230+
except Exception as e:
231+
print(f"⚠️ Could not add token_endpoint column: {e}")
232+
233+
if 'userinfo_endpoint' not in oidc_columns:
234+
try:
235+
if db_dialect == 'postgresql':
236+
alter_sql = 'ALTER TABLE oidc_settings ADD COLUMN userinfo_endpoint VARCHAR(500)'
237+
elif db_dialect == 'mysql':
238+
alter_sql = 'ALTER TABLE oidc_settings ADD COLUMN userinfo_endpoint VARCHAR(500)'
239+
else: # SQLite
240+
alter_sql = 'ALTER TABLE oidc_settings ADD COLUMN userinfo_endpoint VARCHAR(500)'
241+
242+
with db.engine.connect() as conn:
243+
conn.execute(text(alter_sql))
244+
conn.commit()
245+
migrations_applied.append("Added userinfo_endpoint column to oidc_settings")
246+
except Exception as e:
247+
print(f"⚠️ Could not add userinfo_endpoint column: {e}")
248+
249+
# Make discovery_endpoint nullable
250+
try:
251+
# Note: SQLite doesn't support ALTER COLUMN, so this only works for PostgreSQL/MySQL
252+
if db_dialect == 'postgresql':
253+
alter_sql = 'ALTER TABLE oidc_settings ALTER COLUMN discovery_endpoint DROP NOT NULL'
254+
with db.engine.connect() as conn:
255+
conn.execute(text(alter_sql))
256+
conn.commit()
257+
migrations_applied.append("Made discovery_endpoint nullable in oidc_settings")
258+
elif db_dialect == 'mysql':
259+
alter_sql = 'ALTER TABLE oidc_settings MODIFY discovery_endpoint VARCHAR(500)'
260+
with db.engine.connect() as conn:
261+
conn.execute(text(alter_sql))
262+
conn.commit()
263+
migrations_applied.append("Made discovery_endpoint nullable in oidc_settings")
264+
except Exception as e:
265+
print(f"⚠️ Could not make discovery_endpoint nullable (may already be nullable or SQLite): {e}")
266+
197267
if migrations_applied:
198268
print("🔄 Database migrations applied:")
199269
for migration in migrations_applied:

app/forms.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,17 @@ def validate_url(self, field):
191191

192192
class OIDCSettingsForm(FlaskForm):
193193
display_name = StringField('OIDC Display Name', validators=[DataRequired(), Length(min=1, max=100)],
194-
render_kw={'placeholder': 'e.g., Company SSO, Keycloak, etc.'})
194+
render_kw={'placeholder': 'e.g., VoidAuth, Company SSO, etc.'})
195195
client_id = StringField('Client ID', validators=[DataRequired(), Length(min=1, max=500)])
196196
client_secret = StringField('Client Secret', validators=[DataRequired(), Length(min=1, max=500)])
197-
discovery_endpoint = StringField('Discovery Endpoint URL', validators=[DataRequired(), Length(min=1, max=500)],
197+
discovery_endpoint = StringField('Discovery Endpoint URL (Leave blank if blocked/restricted)', validators=[Optional(), Length(max=500)],
198198
render_kw={'placeholder': 'https://your-idp.com/.well-known/openid-configuration'})
199+
authorization_endpoint = StringField('Authorization Endpoint (Required if discovery blocked)', validators=[Optional(), Length(max=500)],
200+
render_kw={'placeholder': 'https://void.madnessshell.com/oidc/authorize'})
201+
token_endpoint = StringField('Token Endpoint (Required if discovery blocked)', validators=[Optional(), Length(max=500)],
202+
render_kw={'placeholder': 'https://void.madnessshell.com/oidc/token'})
203+
userinfo_endpoint = StringField('UserInfo Endpoint (Required if discovery blocked)', validators=[Optional(), Length(max=500)],
204+
render_kw={'placeholder': 'https://void.madnessshell.com/oidc/userinfo'})
199205
user_mapping_field = SelectField('User Mapping Field',
200206
choices=[('username', 'Username'),
201207
('email', 'Email'),
@@ -206,10 +212,24 @@ class OIDCSettingsForm(FlaskForm):
206212
render_kw={'placeholder': 'e.g., preferred_username, sub, etc.'})
207213
is_enabled = BooleanField('Enable OIDC Authentication', default=False)
208214

209-
def validate_discovery_endpoint(self, field):
210-
if not (field.data.startswith('http://') or field.data.startswith('https://')):
211-
raise ValidationError('Discovery endpoint must start with http:// or https://')
212-
213-
def validate_custom_attribute(self, field):
214-
if self.user_mapping_field.data == 'custom' and not field.data:
215-
raise ValidationError('Custom attribute name is required when using custom mapping')
215+
def validate(self, extra_validators=None):
216+
if not super().validate(extra_validators):
217+
return False
218+
219+
# Either discovery endpoint OR all manual endpoints must be provided
220+
has_discovery = bool(self.discovery_endpoint.data)
221+
has_manual = bool(self.authorization_endpoint.data and self.token_endpoint.data and self.userinfo_endpoint.data)
222+
223+
if not has_discovery and not has_manual:
224+
self.discovery_endpoint.errors.append('Either provide Discovery Endpoint OR all three manual endpoints (Authorization, Token, UserInfo)')
225+
return False
226+
227+
if has_discovery and not (self.discovery_endpoint.data.startswith('http://') or self.discovery_endpoint.data.startswith('https://')):
228+
self.discovery_endpoint.errors.append('Discovery endpoint must start with http:// or https://')
229+
return False
230+
231+
if self.user_mapping_field.data == 'custom' and not self.custom_attribute.data:
232+
self.custom_attribute.errors.append('Custom attribute name is required when using custom mapping')
233+
return False
234+
235+
return True

0 commit comments

Comments
 (0)