Skip to content

Commit

Permalink
move business logic to the provider
Browse files Browse the repository at this point in the history
  • Loading branch information
micbar committed Dec 5, 2023
1 parent 62afc6e commit 17e3582
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 28 deletions.
12 changes: 6 additions & 6 deletions services/frontend/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,10 @@ type ServiceAccount struct {

// PasswordPolicy configures reva password policy
type PasswordPolicy struct {
MinCharacters int `yaml:"min_characters,omitempty" env:"FRONTEND_PASSWORD_POLICY_MIN_CHARACTERS" desc:"Define the minimum password length. Defaults to 0 if not set."`
MinLowerCaseCharacters int `yaml:"min_lowercase_characters" env:"FRONTEND_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS" desc:"Define the minimum number of uppercase letters. Defaults to 0 if not set."`
MinUpperCaseCharacters int `yaml:"min_uppercase_characters" env:"FRONTEND_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS" desc:"Define the minimum number of lowercase letters. Defaults to 0 if not set."`
MinDigits int `yaml:"min_digits" env:"FRONTEND_PASSWORD_POLICY_MIN_DIGITS" desc:"Define the minimum number of digits. Defaults to 0 if not set."`
MinSpecialCharacters int `yaml:"min_special_characters" env:"FRONTEND_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS" desc:"Define the minimum number of characters from the special characters list to be present. Defaults to 0 if not set."`
BannedPasswordsList string `yaml:"banned_passwords_list" env:"FRONTEND_PASSWORD_POLICY_BANNED_PASSWORDS_LIST" desc:"Path to the 'banned passwords list' file. See the documentation for more details."`
MinCharacters int `yaml:"min_characters,omitempty" env:"OCIS_PASSWORD_POLICY_MIN_CHARACTERS;FRONTEND_PASSWORD_POLICY_MIN_CHARACTERS" desc:"Define the minimum password length. Defaults to 0 if not set."`
MinLowerCaseCharacters int `yaml:"min_lowercase_characters" env:"OCIS_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS;FRONTEND_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS" desc:"Define the minimum number of uppercase letters. Defaults to 0 if not set."`
MinUpperCaseCharacters int `yaml:"min_uppercase_characters" env:"OCIS_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS;FRONTEND_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS" desc:"Define the minimum number of lowercase letters. Defaults to 0 if not set."`
MinDigits int `yaml:"min_digits" env:"OCIS_PASSWORD_POLICY_MIN_DIGITS;FRONTEND_PASSWORD_POLICY_MIN_DIGITS" desc:"Define the minimum number of digits. Defaults to 0 if not set."`
MinSpecialCharacters int `yaml:"min_special_characters" env:"OCIS_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS;FRONTEND_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS" desc:"Define the minimum number of characters from the special characters list to be present. Defaults to 0 if not set."`
BannedPasswordsList string `yaml:"banned_passwords_list" env:"OCIS_PASSWORD_POLICY_BANNED_PASSWORDS_LIST;FRONTEND_PASSWORD_POLICY_BANNED_PASSWORDS_LIST" desc:"Path to the 'banned passwords list' file. See the documentation for more details."`
}
11 changes: 4 additions & 7 deletions services/graph/pkg/service/v0/driveitems.go
Original file line number Diff line number Diff line change
Expand Up @@ -618,10 +618,9 @@ func (g Graph) DeletePermission(w http.ResponseWriter, r *http.Request) {
}

permissionID, err := url.PathUnescape(chi.URLParam(r, "permissionID"))

if err != nil {
g.logger.Debug().Err(err).Msg("could not parse driveID")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid driveID")
g.logger.Debug().Err(err).Msg("could not parse permissionID")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid permissionID")
return
}

Expand All @@ -632,7 +631,7 @@ func (g Graph) DeletePermission(w http.ResponseWriter, r *http.Request) {
sharedResourceId, err := g.getUserPermissionResourceID(ctx, permissionID)
var errcode *errorcode.Error
if err != nil && errors.As(err, &errcode) && errcode.GetCode() == errorcode.ItemNotFound {
// there is no user share with that ID, so lets check if it is refering to a public link
// there is no user share with that ID, so lets check if it is referring to a public link
isUserPermission = false
sharedResourceId, err = g.getLinkPermissionResourceID(ctx, permissionID)
}
Expand All @@ -644,9 +643,7 @@ func (g Graph) DeletePermission(w http.ResponseWriter, r *http.Request) {

// The resourceID of the shared resource need to match the item ID from the Request Path
// otherwise this is an invalid Request.
if sharedResourceId.GetStorageId() != itemID.GetStorageId() ||
sharedResourceId.GetSpaceId() != itemID.GetSpaceId() ||
sharedResourceId.GetOpaqueId() != itemID.GetOpaqueId() {
if !utils.ResourceIDEqual(sharedResourceId, &itemID) {
g.logger.Debug().Msg("resourceID of shared does not match itemID")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "permissionID and itemID do not match")
return
Expand Down
1 change: 1 addition & 0 deletions services/graph/pkg/service/v0/driveitems_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ var _ = Describe("Driveitems", func() {

Expect(rr.Code).To(Equal(http.StatusNoContent))
})

It("deletes a link permission as expected", func() {
getShareMock := gatewayClient.On("GetShare",
mock.Anything,
Expand Down
34 changes: 30 additions & 4 deletions services/graph/pkg/service/v0/links.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import (
"net/url"
"path"
"strconv"
"time"

rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/go-chi/render"
libregraph "github.com/owncloud/libre-graph-api-go"

"github.com/cs3org/reva/v2/pkg/utils"
"github.com/owncloud/ocis/v2/services/graph/pkg/errorcode"
"github.com/owncloud/ocis/v2/services/graph/pkg/linktype"
)
Expand Down Expand Up @@ -94,7 +94,12 @@ func (g Graph) createLink(ctx context.Context, driveItemID *providerv1beta1.Reso
}
expirationDate, isSet := createLink.GetExpirationDateTimeOk()
if isSet {
req.GetGrant().Expiration = utils.TimeToTS(*expirationDate)
expireTime := parseAndFillUpTime(expirationDate)
if expireTime == nil {
g.logger.Debug().Interface("createLink", createLink).Msg(err.Error())
return nil, errorcode.New(errorcode.InvalidRequest, "invalid expiration date")
}
req.GetGrant().Expiration = expireTime
}

// set displayname and password protected as arbitrary metadata
Expand Down Expand Up @@ -139,7 +144,28 @@ func (g Graph) libreGraphPermissionFromCS3PublicShare(createdLink *link.PublicSh

// set expiration date
if createdLink.GetExpiration() != nil {
perm.SetExpirationDateTime(cs3TimestampToTime(createdLink.GetExpiration()))
perm.SetExpirationDateTime(cs3TimestampToTime(createdLink.GetExpiration()).UTC())
}
return perm, nil
}

func timeToTimestampString(t time.Time) string {
return strconv.FormatInt(t.Unix(), 10)
}

func parseAndFillUpTime(t *time.Time) *types.Timestamp {
if t == nil {
return nil
}

// the link needs to be valid for the whole day
tLink := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
tLink = tLink.Add(23*time.Hour + 59*time.Minute + 59*time.Second)

final := tLink.UnixNano()

return &types.Timestamp{
Seconds: uint64(final / 1000000000),
Nanos: uint32(final % 1000000000),
}
}
4 changes: 4 additions & 0 deletions services/graph/pkg/service/v0/sharedbyme.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,10 @@ func cs3StatusToErrCode(code rpc.Code) (errcode errorcode.ErrorCode) {
errcode = errorcode.ItemNotFound
case rpc.Code_CODE_LOCKED:
errcode = errorcode.ItemIsLocked
case rpc.Code_CODE_INVALID_ARGUMENT:
errcode = errorcode.InvalidRequest
case rpc.Code_CODE_FAILED_PRECONDITION:
errcode = errorcode.InvalidRequest
default:
errcode = errorcode.GeneralException
}
Expand Down
5 changes: 4 additions & 1 deletion services/sharing/pkg/command/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ func Server(cfg *config.Config) *cli.Command {

gr.Add(func() error {
pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid")
rCfg := revaconfig.SharingConfigFromStruct(cfg)
rCfg, err := revaconfig.SharingConfigFromStruct(cfg, logger)
if err != nil {
return err
}
reg := registry.GetRegistry()

runtime.RunWithOptions(rCfg, pidFile,
Expand Down
13 changes: 13 additions & 0 deletions services/sharing/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ type Config struct {
PublicSharingDriver string `yaml:"public_sharing_driver" env:"SHARING_PUBLIC_DRIVER" desc:"Driver to be used to persist public shares. Supported values are 'jsoncs3', 'json' and 'cs3'."`
PublicSharingDrivers PublicSharingDrivers `yaml:"public_sharing_drivers"`
WriteableShareMustHavePassword bool `yaml:"public_sharing_writeableshare_must_have_password" env:"OCIS_SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD;SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD" desc:"Set this to true if you want to enforce passwords on Uploader, Editor or Contributor shares. If not using the global OCIS_SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD, you must define the FRONTEND_OCS_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD in the frontend service."`
PublicShareMustHavePassword bool `yaml:"public_sharing_share_must_have_password" env:"OCIS_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD;SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD" desc:"Set this to true if you want to enforce passwords on all public shares."`
EnableExpiredSharesCleanup bool `yaml:"enable_expired_shares_cleanup"`
Supervised bool `yaml:"-"`
Context context.Context `yaml:"-"`

PasswordPolicy PasswordPolicy `yaml:"password_policy"`
}

type Log struct {
Expand Down Expand Up @@ -151,3 +154,13 @@ type Events struct {
TLSRootCaCertPath string `yaml:"tls_root_ca_cert_path" env:"OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE;SHARING_EVENTS_TLS_ROOT_CA_CERTIFICATE;SHARING_EVENTS_TLS_ROOT_CA_CERT" desc:"The root CA certificate used to validate the server's TLS certificate. If provided SHARING_EVENTS_TLS_INSECURE will be seen as false." deprecationVersion:"4.0.3" removalVersion:"5.0.0" deprecationInfo:"SHARING_EVENTS_TLS_ROOT_CA_CERT changing name for consistency" deprecationReplacement:"SHARING_EVENTS_TLS_ROOT_CA_CERTIFICATE"`
EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;SHARING_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.."`
}

// PasswordPolicy configures reva password policy
type PasswordPolicy struct {
MinCharacters int `yaml:"min_characters,omitempty" env:"OCIS_PASSWORD_POLICY_MIN_CHARACTERS;SHARING_PASSWORD_POLICY_MIN_CHARACTERS" desc:"Define the minimum password length. Defaults to 0 if not set."`
MinLowerCaseCharacters int `yaml:"min_lowercase_characters" env:"OCIS_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS;SHARING_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS" desc:"Define the minimum number of uppercase letters. Defaults to 0 if not set."`
MinUpperCaseCharacters int `yaml:"min_uppercase_characters" env:"OCIS_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS;SHARING_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS" desc:"Define the minimum number of lowercase letters. Defaults to 0 if not set."`
MinDigits int `yaml:"min_digits" env:"OCIS_PASSWORD_POLICY_MIN_DIGITS;SHARING_PASSWORD_POLICY_MIN_DIGITS" desc:"Define the minimum number of digits. Defaults to 0 if not set."`
MinSpecialCharacters int `yaml:"min_special_characters" env:"OCIS_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS;SHARING_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS" desc:"Define the minimum number of characters from the special characters list to be present. Defaults to 0 if not set."`
BannedPasswordsList string `yaml:"banned_passwords_list" env:"OCIS_PASSWORD_POLICY_BANNED_PASSWORDS_LIST;SHARING_PASSWORD_POLICY_BANNED_PASSWORDS_LIST" desc:"Path to the 'banned passwords list' file. See the documentation for more details."`
}
10 changes: 9 additions & 1 deletion services/sharing/pkg/config/defaults/defaultconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,15 @@ func DefaultConfig() *config.Config {
ClusterID: "ocis-cluster",
EnableTLS: false,
},
EnableExpiredSharesCleanup: true,
EnableExpiredSharesCleanup: true,
PublicShareMustHavePassword: true,
PasswordPolicy: config.PasswordPolicy{
MinCharacters: 8,
MinLowerCaseCharacters: 1,
MinUpperCaseCharacters: 1,
MinDigits: 1,
MinSpecialCharacters: 1,
},
}
}

Expand Down
74 changes: 65 additions & 9 deletions services/sharing/pkg/revaconfig/config.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
package revaconfig

import (
"bufio"
"fmt"
"os"
"path/filepath"

"github.com/owncloud/ocis/v2/ocis-pkg/log"

"github.com/owncloud/ocis/v2/ocis-pkg/config/defaults"
"github.com/owncloud/ocis/v2/services/sharing/pkg/config"
)

// SharingConfigFromStruct will adapt an oCIS config struct into a reva mapstructure to start a reva service.
func SharingConfigFromStruct(cfg *config.Config) map[string]interface{} {
func SharingConfigFromStruct(cfg *config.Config, logger log.Logger) (map[string]interface{}, error) {
var bannedPasswordsList map[string]struct{}
var err error
if cfg.PasswordPolicy.BannedPasswordsList != "" {
bannedPasswordsList, err = readMultilineFile(cfg.PasswordPolicy.BannedPasswordsList)
if err != nil {
err = fmt.Errorf("failed to load the banned passwords from a file %s: %w", cfg.PasswordPolicy.BannedPasswordsList, err)
logger.Err(err).Send()
return nil, err
}
}
rcfg := map[string]interface{}{
"shared": map[string]interface{}{
"jwt_secret": cfg.TokenManager.JWTSecret,
Expand Down Expand Up @@ -73,6 +91,17 @@ func SharingConfigFromStruct(cfg *config.Config) map[string]interface{} {
},
},
"publicshareprovider": map[string]interface{}{
"gateway_addr": cfg.Reva.Address,
"writeable_share_must_have_password": cfg.WriteableShareMustHavePassword,
"public_share_must_have_password": cfg.PublicShareMustHavePassword,
"password_policy": map[string]interface{}{
"min_digits": cfg.PasswordPolicy.MinDigits,
"min_characters": cfg.PasswordPolicy.MinCharacters,
"min_lowercase_characters": cfg.PasswordPolicy.MinLowerCaseCharacters,
"min_uppercase_characters": cfg.PasswordPolicy.MinUpperCaseCharacters,
"min_special_characters": cfg.PasswordPolicy.MinSpecialCharacters,
"banned_passwords_list": bannedPasswordsList,
},
"driver": cfg.PublicSharingDriver,
"drivers": map[string]interface{}{
"json": map[string]interface{}{
Expand All @@ -97,13 +126,12 @@ func SharingConfigFromStruct(cfg *config.Config) map[string]interface{} {
"machine_auth_apikey": cfg.PublicSharingDrivers.CS3.SystemUserAPIKey,
},
"jsoncs3": map[string]interface{}{
"gateway_addr": cfg.Reva.Address,
"provider_addr": cfg.PublicSharingDrivers.JSONCS3.ProviderAddr,
"service_user_id": cfg.PublicSharingDrivers.JSONCS3.SystemUserID,
"service_user_idp": cfg.PublicSharingDrivers.JSONCS3.SystemUserIDP,
"machine_auth_apikey": cfg.PublicSharingDrivers.JSONCS3.SystemUserAPIKey,
"writeable_share_must_have_password": cfg.WriteableShareMustHavePassword,
"enable_expired_shares_cleanup": cfg.EnableExpiredSharesCleanup,
"gateway_addr": cfg.Reva.Address,
"provider_addr": cfg.PublicSharingDrivers.JSONCS3.ProviderAddr,
"service_user_id": cfg.PublicSharingDrivers.JSONCS3.SystemUserID,
"service_user_idp": cfg.PublicSharingDrivers.JSONCS3.SystemUserIDP,
"machine_auth_apikey": cfg.PublicSharingDrivers.JSONCS3.SystemUserAPIKey,
"enable_expired_shares_cleanup": cfg.EnableExpiredSharesCleanup,
},
},
},
Expand All @@ -126,5 +154,33 @@ func SharingConfigFromStruct(cfg *config.Config) map[string]interface{} {
},
},
}
return rcfg
return rcfg, nil
}

func readMultilineFile(path string) (map[string]struct{}, error) {
if !fileExists(path) {
path = filepath.Join(defaults.BaseConfigPath(), path)
}
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
scanner := bufio.NewScanner(file)
data := make(map[string]struct{})
for scanner.Scan() {
line := scanner.Text()
if line != "" {
data[line] = struct{}{}
}
}
return data, err
}

func fileExists(path string) bool {
info, err := os.Stat(path)
if err != nil {
return false
}
return !info.IsDir()
}

0 comments on commit 17e3582

Please sign in to comment.