mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-04-27 15:59:29 +00:00
feat(core): initial Google Authenticator support for 2FA
This commit is contained in:
@@ -43,6 +43,10 @@ ADDITIONAL_CPPFLAGS += $(LASSO_CFLAGS)
|
||||
SOGo_LIBRARIES_DEPEND_UPON += $(LASSO_LIBS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAS_LIBRARY_oath), yes)
|
||||
SOGo_LIBRARIES_DEPEND_UPON += $(MFA_LIBS)
|
||||
endif
|
||||
|
||||
ifeq ($(findstring openbsd, $(GNUSTEP_HOST_OS)), openbsd)
|
||||
SOGo_LIBRARIES_DEPEND_UPON += -lcrypto
|
||||
else
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SOGoCache.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2008-2014 Inverse inc.
|
||||
* Copyright (C) 2008-2020 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2006-2016 Inverse inc.
|
||||
Copyright (C) 2006-2020 Inverse inc.
|
||||
|
||||
This file is part of SOGo.
|
||||
|
||||
@@ -120,6 +120,7 @@
|
||||
|
||||
- (BOOL) isSuperUser;
|
||||
- (BOOL) canAuthenticate;
|
||||
- (NSString *) googleAuthenticatorKey;
|
||||
|
||||
/* resource */
|
||||
- (BOOL) isResource;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2006-2016 Inverse inc.
|
||||
Copyright (C) 2006-2020 Inverse inc.
|
||||
|
||||
This file is part of SOGo.
|
||||
|
||||
@@ -39,6 +39,10 @@
|
||||
#import "SOGoUserSettings.h"
|
||||
#import "WOResourceManager+SOGo.h"
|
||||
|
||||
#if defined(MFA_CONFIG)
|
||||
#include <liboath/oath.h>
|
||||
#endif
|
||||
|
||||
#import "SOGoUser.h"
|
||||
|
||||
@implementation SoUser (SOGoExtension)
|
||||
@@ -1079,6 +1083,34 @@
|
||||
return [authValue boolValue];
|
||||
}
|
||||
|
||||
- (NSString *) googleAuthenticatorKey
|
||||
{
|
||||
#if defined(MFA_CONFIG)
|
||||
NSString *key, *result;
|
||||
const char *s;
|
||||
char *secret;
|
||||
|
||||
size_t s_len, secret_len;
|
||||
|
||||
key = [[[self userSettings] userSalt] substringToIndex: 12];
|
||||
s = [key UTF8String];
|
||||
s_len = strlen(s);
|
||||
|
||||
oath_init();
|
||||
oath_base32_encode(s,s_len, &secret, &secret_len);
|
||||
oath_done();
|
||||
|
||||
result = [[NSString alloc] initWithBytesNoCopy: secret
|
||||
length: secret_len
|
||||
encoding: NSASCIIStringEncoding
|
||||
freeWhenDone: YES];
|
||||
|
||||
return [result autorelease];
|
||||
#else
|
||||
return nil;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* resource */
|
||||
- (BOOL) isResource
|
||||
{
|
||||
|
||||
@@ -133,6 +133,9 @@ extern NSString *SOGoWeekStartFirstFullWeek;
|
||||
- (void) setAnimationMode: (NSString *) newValue;
|
||||
- (NSString *) animationMode;
|
||||
|
||||
- (BOOL) googleAuthenticatorEnabled;
|
||||
- (void) setGoogleAuthenticatorEnabled: (BOOL) newValue;
|
||||
|
||||
- (void) setMailComposeWindow: (NSString *) newValue;
|
||||
- (NSString *) mailComposeWindow;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SOGoUserDefaults.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2009-2017 Inverse inc.
|
||||
* Copyright (C) 2009-2020 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -542,6 +542,16 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
|
||||
return [self stringForKey: @"SOGoAnimationMode"];
|
||||
}
|
||||
|
||||
- (BOOL) googleAuthenticatorEnabled
|
||||
{
|
||||
return [self boolForKey: @"SOGoGoogleAuthenticatorEnabled"];
|
||||
}
|
||||
|
||||
- (void) setGoogleAuthenticatorEnabled: (BOOL) newValue
|
||||
{
|
||||
[self setBool: newValue forKey: @"SOGoGoogleAuthenticatorEnabled"];
|
||||
}
|
||||
|
||||
- (void) setMailComposeWindow: (NSString *) newValue
|
||||
{
|
||||
[self setObject: newValue forKey: @"SOGoMailComposeWindow"];
|
||||
|
||||
@@ -8,3 +8,7 @@ ADDITIONAL_CPPFLAGS += \
|
||||
ifeq ($(HAS_LIBRARY_lasso), yes)
|
||||
ADDITIONAL_CPPFLAGS += $(LASSO_CFLAGS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAS_LIBRARY_oath), yes)
|
||||
ADDITIONAL_LDFLAGS += $(MFA_LIBS)
|
||||
endif
|
||||
|
||||
@@ -50,6 +50,10 @@
|
||||
#import <SOGo/SOGoUserManager.h>
|
||||
#import <SOGo/SOGoWebAuthenticator.h>
|
||||
|
||||
#if defined(MFA_CONFIG)
|
||||
#include <liboath/oath.h>
|
||||
#endif
|
||||
|
||||
#import "SOGoRootPage.h"
|
||||
|
||||
@implementation SOGoRootPage
|
||||
@@ -182,7 +186,7 @@
|
||||
SOGoUserDefaults *ud;
|
||||
SOGoUser *loggedInUser;
|
||||
NSDictionary *params;
|
||||
NSString *username, *password, *language, *domain, *remoteHost;
|
||||
NSString *username, *password, *language, *domain, *remoteHost, *verificationCode;
|
||||
NSArray *supportedLanguages, *creds;
|
||||
|
||||
SOGoPasswordPolicyError err;
|
||||
@@ -198,6 +202,7 @@
|
||||
|
||||
username = [params objectForKey: @"userName"];
|
||||
password = [params objectForKey: @"password"];
|
||||
verificationCode = [params objectForKey: @"verificationCode"];
|
||||
language = [params objectForKey: @"language"];
|
||||
rememberLogin = [[params objectForKey: @"rememberLogin"] boolValue];
|
||||
domain = [params objectForKey: @"domain"];
|
||||
@@ -223,12 +228,70 @@
|
||||
// the DomainLessLogin situation, so we would NOT add the domain. -getUIDForEmail
|
||||
// has all the logic for this, so lets use it.
|
||||
if ([domain isNotNull])
|
||||
{
|
||||
username = [[SOGoUserManager sharedUserManager] getUIDForEmail: username];
|
||||
}
|
||||
username = [[SOGoUserManager sharedUserManager] getUIDForEmail: username];
|
||||
|
||||
loggedInUser = [SOGoUser userWithLogin: username];
|
||||
|
||||
#if defined(MFA_CONFIG)
|
||||
if ([[loggedInUser userDefaults] googleAuthenticatorEnabled])
|
||||
{
|
||||
if ([verificationCode length] == 6 && [verificationCode unsignedIntValue] > 0)
|
||||
{
|
||||
unsigned int code;
|
||||
const char *real_secret;
|
||||
char *secret;
|
||||
|
||||
size_t secret_len;
|
||||
|
||||
const auto time_step = OATH_TOTP_DEFAULT_TIME_STEP_SIZE;
|
||||
const auto digits = 6;
|
||||
|
||||
real_secret = [[loggedInUser googleAuthenticatorKey] UTF8String];
|
||||
|
||||
auto result = oath_init();
|
||||
auto t = time(NULL);
|
||||
auto left = time_step - (t % time_step);
|
||||
|
||||
char otp[digits + 1];
|
||||
|
||||
oath_base32_decode (real_secret,
|
||||
strlen(real_secret),
|
||||
&secret, &secret_len);
|
||||
|
||||
result = oath_totp_generate2(secret,
|
||||
secret_len,
|
||||
t,
|
||||
time_step,
|
||||
OATH_TOTP_DEFAULT_START_TIME,
|
||||
digits,
|
||||
0,
|
||||
otp);
|
||||
|
||||
sscanf(otp, "%u", &code);
|
||||
|
||||
oath_done();
|
||||
free(secret);
|
||||
|
||||
if (code != [verificationCode unsignedIntValue])
|
||||
{
|
||||
[self logWithFormat: @"Invalid Google Authenticator key for '%@'", username];
|
||||
json = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: 1]
|
||||
forKey: @"GoogleAuthenticatorInvalidKey"];
|
||||
return [self responseWithStatus: 403
|
||||
andJSONRepresentation: json];
|
||||
}
|
||||
} // if ([verificationCode length] == 6 && [verificationCode unsignedIntValue] > 0)
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Missing Google Authenticator key for '%@', asking it..", username];
|
||||
json = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: 1]
|
||||
forKey: @"GoogleAuthenticatorMissingKey"];
|
||||
return [self responseWithStatus: 202
|
||||
andJSONRepresentation: json];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
json = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[loggedInUser cn], @"cn",
|
||||
[NSNumber numberWithInt: expire], @"expire",
|
||||
@@ -265,8 +328,8 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat:@"Login from '%@' for user '%@' might not have worked - password policy: %d grace: %d expire: %d bound: %d",
|
||||
remoteHost, username, err, grace, expire, b];
|
||||
[self logWithFormat: @"Login from '%@' for user '%@' might not have worked - password policy: %d grace: %d expire: %d bound: %d",
|
||||
remoteHost, username, err, grace, expire, b];
|
||||
|
||||
response = [self _responseWithLDAPPolicyError: err];
|
||||
}
|
||||
@@ -639,4 +702,13 @@
|
||||
return response;
|
||||
}
|
||||
|
||||
- (BOOL) isGoogleAuthenticatorEnabled
|
||||
{
|
||||
#if defined(MFA_CONFIG)
|
||||
return YES;
|
||||
#else
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
@end /* SOGoRootPage */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* UIxJSONPreferences.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2017 Inverse inc.
|
||||
* Copyright (C) 2007-2020 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -171,6 +171,9 @@ static SoProduct *preferencesProduct = nil;
|
||||
if (![[defaults source] objectForKey: @"SOGoAnimationMode"])
|
||||
[[defaults source] setObject: [defaults animationMode] forKey: @"SOGoAnimationMode"];
|
||||
|
||||
if (![[defaults source] objectForKey: @"SOGoGoogleAuthenticatorEnabled"])
|
||||
[[defaults source] setObject: [NSNumber numberWithBool: NO] forKey: @"SOGoGoogleAuthenticatorEnabled"];
|
||||
|
||||
//
|
||||
// Default Calendar preferences
|
||||
//
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* UIxPreferences.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2019 Inverse inc.
|
||||
* Copyright (C) 2007-2020 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -1021,6 +1021,20 @@ static NSArray *reminderValues = nil;
|
||||
return [NSString stringWithString: SOGoVersion];
|
||||
}
|
||||
|
||||
- (BOOL) isGoogleAuthenticatorEnabled
|
||||
{
|
||||
#if defined(MFA_CONFIG)
|
||||
return YES;
|
||||
#else
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (NSString *) googleAuthenticatorKey
|
||||
{
|
||||
return [[context activeUser] googleAuthenticatorKey];
|
||||
}
|
||||
|
||||
//
|
||||
// Used internally
|
||||
//
|
||||
|
||||
@@ -52,6 +52,16 @@
|
||||
<input type="password" ng-model="app.creds.password" ng-required="true"/>
|
||||
</md-input-container>
|
||||
|
||||
<var:if condition="isGoogleAuthenticatorEnabled">
|
||||
<md-input-container class="md-block"
|
||||
ng-show="app.showGoogleAuthenticatorCode">
|
||||
<label><var:string label:value="Google Authenticator
|
||||
Verification Code"/></label>
|
||||
<md-icon>email</md-icon>
|
||||
<input type="text" ng-model="app.creds.verificationCode" ng-required="false"/>
|
||||
</md-input-container>
|
||||
</var:if>
|
||||
|
||||
<!-- LANGUAGES SELECT -->
|
||||
<div layout="row" layout-align="start end">
|
||||
<md-icon>language</md-icon>
|
||||
|
||||
@@ -231,6 +231,27 @@
|
||||
</md-radio-group>
|
||||
</md-input-container>
|
||||
|
||||
<var:if condition="isGoogleAuthenticatorEnabled">
|
||||
<md-checkbox flex="20"
|
||||
ng-model="app.preferences.defaults.SOGoGoogleAuthenticatorEnabled"
|
||||
ng-true-value="1"
|
||||
ng-false-value="0"
|
||||
label:aria-label="Enable two-factor authentication using Google Authenticator">
|
||||
<var:string label:value="Enable two-factor authentication using Google Authenticator"/>
|
||||
</md-checkbox>
|
||||
<input type="text"
|
||||
ng-readonly="true"
|
||||
ng-show="app.preferences.defaults.SOGoGoogleAuthenticatorEnabled == 1"
|
||||
var:value="googleAuthenticatorKey"/>
|
||||
<label
|
||||
ng-show="app.preferences.defaults.SOGoGoogleAuthenticatorEnabled
|
||||
== 1"><var:string label:value="You must enter
|
||||
this key into your Google Authenticator
|
||||
application. If you do not and you log out
|
||||
you will not be able to access SOGo
|
||||
again."/></label>
|
||||
</var:if>
|
||||
|
||||
</div>
|
||||
</md-content>
|
||||
</md-tab>
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
var d = $q.defer(),
|
||||
username = data.username,
|
||||
password = data.password,
|
||||
verificationCode = data.verificationCode,
|
||||
domain = data.domain,
|
||||
language,
|
||||
rememberLogin = data.rememberLogin ? 1 : 0;
|
||||
@@ -80,6 +81,7 @@
|
||||
data: {
|
||||
userName: username,
|
||||
password: password,
|
||||
verificationCode: verificationCode,
|
||||
domain: domain,
|
||||
language: language,
|
||||
rememberLogin: rememberLogin
|
||||
@@ -91,8 +93,12 @@
|
||||
d.reject({error: l('cookiesNotEnabled')});
|
||||
}
|
||||
else {
|
||||
// Check for Google Authenticator 2FA
|
||||
if (typeof data.GoogleAuthenticatorMissingKey != 'undefined' && response.status == 202) {
|
||||
d.resolve({gamissingkey: 1});
|
||||
}
|
||||
// Check password policy
|
||||
if (typeof data.expire != 'undefined' && typeof data.grace != 'undefined') {
|
||||
else if (typeof data.expire != 'undefined' && typeof data.grace != 'undefined') {
|
||||
if (data.expire < 0 && data.grace > 0) {
|
||||
d.reject({grace: data.grace});
|
||||
//showPasswordDialog('grace', createPasswordGraceDialog, data['grace']);
|
||||
@@ -110,7 +116,10 @@
|
||||
}
|
||||
}, function(response) {
|
||||
var msg, perr, data = response.data;
|
||||
if (data && data.LDAPPasswordPolicyError) {
|
||||
if (data && data.GoogleAuthenticatorInvalidKey) {
|
||||
msg = l('You provided an invalid Google Authenticator key.');
|
||||
}
|
||||
else if (data && data.LDAPPasswordPolicyError) {
|
||||
perr = data.LDAPPasswordPolicyError;
|
||||
if (perr == passwordPolicyConfig.PolicyNoError) {
|
||||
msg = l('Wrong username or password.');
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
if (/\blanguage=/.test($window.location.search))
|
||||
this.creds.language = $window.language;
|
||||
this.loginState = false;
|
||||
this.showGoogleAuthenticatorCode = false;
|
||||
|
||||
// Show login once everything is initialized
|
||||
this.showLogin = false;
|
||||
@@ -33,16 +34,23 @@
|
||||
vm.loginState = 'authenticating';
|
||||
Authentication.login(vm.creds)
|
||||
.then(function(data) {
|
||||
vm.loginState = 'logged';
|
||||
vm.cn = data.cn;
|
||||
|
||||
// Let the user see the succesfull message before reloading the page
|
||||
$timeout(function() {
|
||||
if ($window.location.href === data.url)
|
||||
$window.location.reload(true);
|
||||
else
|
||||
$window.location.href = data.url;
|
||||
}, 1000);
|
||||
if (typeof data.gamissingkey != 'undefined' && data.gamissingkey == 1) {
|
||||
vm.showGoogleAuthenticatorCode = true;
|
||||
vm.loginState = 'error';
|
||||
}
|
||||
else {
|
||||
vm.loginState = 'logged';
|
||||
vm.cn = data.cn;
|
||||
|
||||
// Let the user see the succesfull message before reloading the page
|
||||
$timeout(function() {
|
||||
if ($window.location.href === data.url)
|
||||
$window.location.reload(true);
|
||||
else
|
||||
$window.location.href = data.url;
|
||||
}, 1000);
|
||||
}
|
||||
}, function(msg) {
|
||||
vm.loginState = 'error';
|
||||
vm.errorMessage = msg.error;
|
||||
|
||||
24
configure
vendored
24
configure
vendored
@@ -25,6 +25,7 @@ ARG_CFGSSL="auto"
|
||||
ARG_WITH_DEBUG=1
|
||||
ARG_WITH_STRIP=0
|
||||
ARG_ENABLE_SAML2=0
|
||||
ARG_ENABLE_MFA=0
|
||||
ARG_WITH_LDAP_CONFIG=0
|
||||
|
||||
GNUSTEP_INSTALLATION_DOMAIN="LOCAL"
|
||||
@@ -76,6 +77,7 @@ Installation directories:
|
||||
--enable-strip turn on stripping of debug symbols
|
||||
--with-ssl=SSL specify ssl library (none, ssl, gnutls, auto) [auto]
|
||||
--enable-saml2 enable support for SAML2 authentication (requires liblasso)
|
||||
--enable-mfa enable multi-factor authentication (requires liboath)
|
||||
|
||||
--enable-ldap-config enable LDAP based configuration of SOGo
|
||||
|
||||
@@ -106,6 +108,11 @@ printParas() {
|
||||
else
|
||||
echo " saml2 support: no";
|
||||
fi
|
||||
if test $ARG_ENABLE_MFA = 1; then
|
||||
echo " mfa support: yes";
|
||||
else
|
||||
echo " mfa support: no";
|
||||
fi
|
||||
if test $ARG_WITH_LDAP_CONFIG = 1; then
|
||||
echo " ldap-based configuration: yes";
|
||||
else
|
||||
@@ -312,6 +319,11 @@ genConfigMake() {
|
||||
cfgwrite "saml2_config:=yes"
|
||||
fi
|
||||
|
||||
if test $ARG_ENABLE_MFA = 1; then
|
||||
cfgwrite "ADDITIONAL_CPPFLAGS += -DMFA_CONFIG=1"
|
||||
cfgwrite "mfa_config:=yes"
|
||||
fi
|
||||
|
||||
if test $ARG_WITH_LDAP_CONFIG = 1; then
|
||||
cfgwrite "ADDITIONAL_CPPFLAGS += -DLDAP_CONFIG=1"
|
||||
cfgwrite "ldap_config:=yes"
|
||||
@@ -324,7 +336,7 @@ checkLinking() {
|
||||
# library-name => $1, type => $2
|
||||
local oldpwd="${PWD}"
|
||||
local tmpdir=".configure-test-$$"
|
||||
|
||||
|
||||
mkdir $tmpdir
|
||||
cd $tmpdir
|
||||
cat > dummytool.c <<EOF
|
||||
@@ -389,6 +401,12 @@ checkDependencies() {
|
||||
cfgwrite "LASSO_LIBS := $lasso_libs"
|
||||
fi;
|
||||
fi
|
||||
if test "x$ARG_ENABLE_MFA" = "x1"; then
|
||||
checkLinking "oath" required;
|
||||
if test $? = 0; then
|
||||
cfgwrite "MFA_LIBS := -loath"
|
||||
fi;
|
||||
fi
|
||||
if test "x$ARG_CFGSSL" = "xauto"; then
|
||||
checkLinking "ssl" optional;
|
||||
if test $? != 0; then
|
||||
@@ -480,7 +498,9 @@ processOption() {
|
||||
"x--enable-saml2")
|
||||
ARG_ENABLE_SAML2=1
|
||||
;;
|
||||
|
||||
"x--enable-mfa")
|
||||
ARG_ENABLE_MFA=1
|
||||
;;
|
||||
"x--enable-ldap-config")
|
||||
ARG_WITH_LDAP_CONFIG=1
|
||||
;;
|
||||
|
||||
@@ -7,12 +7,28 @@ DESTDIR=$(CURDIR)/debian/tmp
|
||||
|
||||
SAML2_CONFIG=--enable-saml2
|
||||
|
||||
#ifeq ($(DIST_CODENAME), stretch)
|
||||
MFA_CONFIG=--enable-mfa
|
||||
#endif
|
||||
|
||||
#ifeq ($(DIST_CODENAME), buster)
|
||||
MFA_CONFIG=--enable-mfa
|
||||
#endif
|
||||
|
||||
#ifeq ($(DIST_CODENAME), xenial)
|
||||
MFA_CONFIG=--enable-mfa
|
||||
#endif
|
||||
|
||||
#ifeq ($(DIST_CODENAME), bionic)
|
||||
MFA_CONFIG=--enable-mfa
|
||||
#endif
|
||||
|
||||
include /etc/GNUstep/GNUstep.conf
|
||||
include /usr/share/GNUstep/Makefiles/common.make
|
||||
|
||||
config.make: configure
|
||||
dh_testdir
|
||||
./configure $(SAML2_CONFIG)
|
||||
./configure $(SAML2_CONFIG) $(MFA_CONFIG)
|
||||
|
||||
#Architecture
|
||||
build: build-arch
|
||||
|
||||
@@ -34,11 +34,17 @@ BuildRequires: gcc-objc gnustep-base gnustep-make sope%{sope_major_version}%{so
|
||||
|
||||
# saml is enabled everywhere except on el5 since its glib2 is prehistoric
|
||||
%define saml2_cfg_opts "--enable-saml2"
|
||||
%define mfa_cfg_opts "--enable-mfa"
|
||||
%{?el5:%define saml2_cfg_opts ""}
|
||||
%{?el5:%define mfa_cfg_opts ""}
|
||||
%{?el6:%define mfa_cfg_opts ""}
|
||||
%{?el6:Requires: lasso}
|
||||
%{?el6:BuildRequires: lasso-devel}
|
||||
%{?el7:Requires: lasso}
|
||||
%{?el7:BuildRequires: lasso-devel}
|
||||
%{?el7:Requires: liboath}
|
||||
%{?el7:BuildRequires: liboath-devel}
|
||||
|
||||
|
||||
%description
|
||||
SOGo is a groupware server built around OpenGroupware.org (OGo) and
|
||||
@@ -155,7 +161,7 @@ rm -fr ${RPM_BUILD_ROOT}
|
||||
%else
|
||||
. /usr/share/GNUstep/Makefiles/GNUstep.sh
|
||||
%endif
|
||||
./configure %saml2_cfg_opts
|
||||
./configure %saml2_cfg_opts %mfa_cfg_opts
|
||||
|
||||
case %{_target_platform} in
|
||||
ppc64-*)
|
||||
@@ -376,6 +382,9 @@ fi
|
||||
|
||||
# ********************************* changelog *************************
|
||||
%changelog
|
||||
* Thu Apr 30 2020 Inverse inc. <support@inverse.ca>
|
||||
- added liboath requirements for RHELv7
|
||||
|
||||
* Thu Mar 31 2015 Inverse inc. <support@inverse.ca>
|
||||
- Change script start sogod for systemd
|
||||
|
||||
|
||||
Reference in New Issue
Block a user