unit fpoauth2ini;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, fpoauth2, inifiles;

Type

  { TFPOAuth2IniStore }

  TFPOAuth2IniStore = Class(TAbstracTOAuth2ConfigStore)
  private
    FApplicationSection: String;
    FConfigFileName: String;
    FFileName: String;
    FProviderSection: String;
    FSessionFileName: String;
    FUserSection: String;
    procedure EnsureFileName;
    Procedure EnsureConfigSections;
    procedure SetSessionSectionUser(AUser: String);
  Protected
    Function DetectSessionFileName : String;
    Function EnsureUserSession(ASession: TOAuth2Session): Boolean; virtual;
  Public
    Constructor Create(AOwner : TComponent); override;
    Destructor Destroy; override;
    Procedure SaveConfigToIni(AIni : TCustomIniFile;AConfig : TOAuth2Config); virtual;
    Procedure LoadConfigFromIni(AIni : TCustomIniFile;AConfig : TOAuth2Config); virtual;
    Procedure SaveSessionToIni(AIni : TCustomIniFile;ASession : TOAuth2Session); virtual;
    Procedure LoadSessionFromIni(AIni : TCustomIniFile;ASession : TOAuth2Session); virtual;
    Procedure SaveConfig(AConfig : TOAuth2Config); override;
    Procedure LoadConfig(AConfig : TOAuth2Config); override;
    Procedure LoadSession(ASession : TOAuth2Session;Const AUser : String); override;
    Procedure SaveSession(Asession : TOAuth2Session;Const AUser : String); override;
  Published
    // Static configuration, readable by web process. Default is app config file.
    Property ConfigFileName: String Read FConfigFileName Write FConfigFileName;
    // Per-user (session) configuration, writeable by webprocess. Default is temp dir+'oauth-'+ConfigFileName
    Property SessionFileName: String Read FSessionFileName Write FSessionFileName;
    // Name of application section (Application)
    Property ApplicationSection : String Read FApplicationSection Write FApplicationSection;
    // Name of provider section (Provider)
    Property ProviderSection : String Read FProviderSection Write FProviderSection;
    // Name of User session section (username from ID)
    Property UserSessionSection : String Read FUserSection Write FUserSection;
  end;


implementation

uses typinfo;

Const
  // Default sections.

  SApplication = 'Application';
  SProvider    = 'Provider';

Const
  SClient            = 'Client';
  SAuth              = 'Authorization';

  KeyenableGZIP      = 'EnableGZIP';
  KeyApplicationName = 'ApplicationName';
  KeyMethod          = 'Method';

  // Application keys
  KeyClientID        = 'client_id';
  KeyClientSecret    = 'client_secret';
  KeyRedirectURI     = 'redirect_uri';
  KeyAccessType      = 'access_type';
  KeyDeveloperKey    = 'DeveloperKey';
  KeyOpenIDRealm     = 'OpenIDRealm';

  // Provider keys
  KeyHostedDomain    = 'HostedDomain';
  KeyTokenURL        = 'TokenURL';
  KeyAuthURL         = 'AuthURL';
  KeyAuthScope       = 'AuthScope';

  // User keys
  KeyAccessToken     = 'access_token';
  KeyRefreshToken    = 'refresh_token';
  KeyTokenType       = 'token_type';
  KeyExpiresAt       = 'expires_at';
  KeyExpiresIn       = 'expires_in';
  KeyLoginHint       = 'login_hint';
  KeyIDToken         = 'id_token';

{ TFPOAuth2IniStore }

Procedure Touch(FN : String);

begin
//  FileClose(FileCreate('/tmp/logs/'+fn));
end;

procedure TFPOAuth2IniStore.EnsureFileName;

begin
  If (ConfigFileName='') then
    ConfigFileName:=GetAppConfigFile(True);
  if SessionFIleName='' then
    SessionFileName:=GetTempDir(True)+'oauth-'+ExtractFileName(GetAppConfigFile(True));
end;

procedure TFPOAuth2IniStore.EnsureConfigSections;
begin
  if (ApplicationSection='') then
    ApplicationSection:=SApplication;
  if (ProviderSection='') then
    ProviderSection:=SProvider;
end;

function TFPOAuth2IniStore.DetectSessionFileName: String;
begin
  Result:=FSessionFileName;
  If Result='' then
    Result:=ConfigFileName
end;

procedure TFPOAuth2IniStore.SaveConfigToIni(AIni: TCustomIniFile; AConfig: TOAuth2Config);

begin
  EnsureConfigSections;
  Touch('saveconfigfomini');
  Touch('saveconfigfomini-app-'+ApplicationSection);
  Touch('saveconfigfomini-provider-'+ProviderSection);
  With AIni,AConfig do
    begin
    WriteString(ApplicationSection,KeyClientID,ClientID);
    WriteString(ApplicationSection,KeyClientSecret,ClientSecret);
    WriteString(ApplicationSection,KeyRedirectURI,RedirectURI);
    WriteString(ApplicationSection,KeyDeveloperKey,DeveloperKey);
    WriteString(ApplicationSection,KeyOpenIDRealm,OpenIDRealm);
    WriteString(ApplicationSection,KeyAccessType,GetEnumName(Typeinfo(TAccessType),Ord(AccessType)));
    WriteString(ProviderSection,KeyHostedDomain,HostedDomain);
    WriteString(ProviderSection,KeyTokenURL,TokenURL);
    WriteString(ProviderSection,KeyAuthURL,AuthURL);
    WriteString(ProviderSection,KeyAuthScope,AuthScope);
    end;
end;

procedure TFPOAuth2IniStore.LoadConfigFromIni(AIni: TCustomIniFile;
  AConfig: TOAuth2Config);

Var
  S : String;
  i : Integer;

begin
  EnsureConfigSections;
  Touch('Loadconfigfomini');
  Touch('Loadconfigfomini-app-'+ApplicationSection);
  Touch('Loadconfigfomini-provider-'+ProviderSection);
  With AIni,AConfig do
    begin
    ClientID:=ReadString(ApplicationSection,KeyClientID,ClientID);
    ClientSecret:=ReadString(ApplicationSection,KeyClientSecret,ClientSecret);
    RedirectURI:=AIni.ReadString(ApplicationSection,KeyRedirectURI,RedirectURI);
    DeveloperKey:=AIni.ReadString(ApplicationSection,KeyDeveloperKey,DeveloperKey);
    OpenIDRealm:=AIni.ReadString(ApplicationSection,KeyOpenIDRealm,OpenIDRealm);
    S:=AIni.ReadString(ApplicationSection,KeyAccessType,GetEnumName(Typeinfo(TAccessType),Ord(AccessType)));
    i:= GetEnumValue(TYpeinfo(TAccessType),S);
    if (I<>-1) then
      AccessType:=TAccessType(i);
    HostedDomain:=ReadString(ProviderSection,KeyHostedDomain,HostedDomain);
    TokenURL:=ReadString(ProviderSection,KeyTokenURL,TokenURL);
    AuthURL:=ReadString(ProviderSection,KeyAuthURL,AuthURL);
    AuthScope:=ReadString(ProviderSection,KeyAuthScope,AuthScope);
    end;
end;

procedure TFPOAuth2IniStore.SaveSessionToIni(AIni: TCustomIniFile;
  ASession: TOAuth2Session);
begin
  Touch('savesessiontoini'+usersessionsection);
  With AIni,ASession do
    begin
    WriteString(UserSessionSection,KeyLoginHint,LoginHint);
    WriteString(UserSessionSection,KeyAccessToken,AccessToken);
    WriteString(UserSessionSection,KeyRefreshToken,RefreshToken);
    WriteString(UserSessionSection,KeyTokenType,AuthTokenType);
    WriteInteger(UserSessionSection,KeyExpiresIn,AuthExpiryPeriod);
    WriteDateTime(UserSessionSection,KeyExpiresAt,AuthExpires);
    WriteString(UserSessionSection,KeyIDToken,IDToken);
    end;
end;

procedure TFPOAuth2IniStore.LoadSessionFromIni(AIni: TCustomIniFile;
  ASession: TOAuth2Session);
begin
  Touch('loadsessionini-'+usersessionsection);
  With AIni,ASession do
    begin
    LoginHint:=ReadString(UserSessionSection,KeyLoginHint,LoginHint);
    AccessToken:=ReadString(UserSessionSection,KeyAccessToken,AccessToken);
    RefreshToken:=ReadString(UserSessionSection,KeyRefreshToken,RefreshToken);
    AuthTokenType:=ReadString(UserSessionSection,KeyTokenType,AuthTokenType);
    AuthExpiryPeriod:=ReadInteger(UserSessionSection,KeyExpiresIn,0);
    AuthExpires:=ReadDateTime(UserSessionSection,KeyExpiresAt,AuthExpires);
    IDToken:=ReadString(UserSessionSection,KeyIDToken,'');
    end;
end;

procedure TFPOAuth2IniStore.SaveConfig(AConfig: TOAuth2Config);

Var
  Ini : TMemIniFile;

begin
  Touch('saveconfig');
  EnsureFileName;
  Ini:=TMemIniFile.Create(ConfigFileName);
  try
    SaveConfigToIni(Ini,AConfig);
    Ini.UpdateFile;
  finally
    Ini.Free;
  end;
end;

procedure TFPOAuth2IniStore.LoadConfig(AConfig: TOAuth2Config);
Var
  Ini : TMemIniFile;

begin
  Touch('loadconfig');
  EnsureFileName;
  Ini:=TMemIniFile.Create(ConfigFileName);
  try
    LoadConfigFromIni(Ini,AConfig);
  finally
    Ini.Free;
  end;
end;

function TFPOAuth2IniStore.EnsureUserSession(ASession: TOAuth2Session): Boolean;

begin
  Result:=(UserSessionSection<>'');
end;

constructor TFPOAuth2IniStore.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  EnsureConfigSections;
end;

destructor TFPOAuth2IniStore.Destroy;
begin
  inherited Destroy;
end;

procedure TFPOAuth2IniStore.SetSessionSectionUser(AUser : String);

begin
  If (UserSessionSection='') then
    begin
    if (AUser='') then
       AUser:='anonymous';
    UserSessionSection:='session_'+AUser;
    end;
end;

procedure TFPOAuth2IniStore.LoadSession(ASession: TOAuth2Session;
  const AUser: String);

Var
  Ini : TMemIniFile;

begin
  EnsureFileName;
  SetSessionSectionUser(AUser);
  If not EnsureUserSession(ASession) then
    Exit;
  Ini:=TMemIniFile.Create(SessionFileName);
  try
    LoadSessionFromIni(Ini,ASession);
  finally
    Ini.Free;
  end;
end;

procedure TFPOAuth2IniStore.SaveSession(Asession: TOAuth2Session;
  const AUser: String);

Var
  Ini : TMemIniFile;

begin
  EnsureFileName;
  SetSessionSectionUser(AUser);
  If not EnsureUserSession(ASession) then
    Exit;
  Ini:=TMemIniFile.Create(SessionFileName);
  try
    SaveSessionToIni(Ini,ASession);
    Ini.UpdateFile;
  finally
    Ini.Free;
  end;
end;

end.

