diff --git a/api/graphql/generated.go b/api/graphql/generated.go index 5611b953..a2a28871 100644 --- a/api/graphql/generated.go +++ b/api/graphql/generated.go @@ -137,15 +137,16 @@ type ComplexityRoot struct { } Query struct { - Album func(childComplexity int, id int) int - MyAlbums func(childComplexity int, filter *models.Filter, onlyRoot *bool, showEmpty *bool) int - MyPhotos func(childComplexity int, filter *models.Filter) int - MyUser func(childComplexity int) int - Photo func(childComplexity int, id int) int - Search func(childComplexity int, query string, limitPhotos *int, limitAlbums *int) int - ShareToken func(childComplexity int, token string, password *string) int - SiteInfo func(childComplexity int) int - User func(childComplexity int, filter *models.Filter) int + Album func(childComplexity int, id int) int + MyAlbums func(childComplexity int, filter *models.Filter, onlyRoot *bool, showEmpty *bool) int + MyPhotos func(childComplexity int, filter *models.Filter) int + MyUser func(childComplexity int) int + Photo func(childComplexity int, id int) int + Search func(childComplexity int, query string, limitPhotos *int, limitAlbums *int) int + ShareToken func(childComplexity int, token string, password *string) int + ShareTokenRequiresPassword func(childComplexity int, token string) int + SiteInfo func(childComplexity int) int + User func(childComplexity int, filter *models.Filter) int } ScannerResult struct { @@ -228,6 +229,7 @@ type QueryResolver interface { MyPhotos(ctx context.Context, filter *models.Filter) ([]*models.Photo, error) Photo(ctx context.Context, id int) (*models.Photo, error) ShareToken(ctx context.Context, token string, password *string) (*models.ShareToken, error) + ShareTokenRequiresPassword(ctx context.Context, token string) (bool, error) Search(ctx context.Context, query string, limitPhotos *int, limitAlbums *int) (*models.SearchResult, error) } type ShareTokenResolver interface { @@ -827,6 +829,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.ShareToken(childComplexity, args["token"].(string), args["password"].(*string)), true + case "Query.shareTokenRequiresPassword": + if e.complexity.Query.ShareTokenRequiresPassword == nil { + break + } + + args, err := ec.field_Query_shareTokenRequiresPassword_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.ShareTokenRequiresPassword(childComplexity, args["token"].(string)), true + case "Query.siteInfo": if e.complexity.Query.SiteInfo == nil { break @@ -1108,6 +1122,7 @@ type Query { photo(id: Int!): Photo! shareToken(token: String!, password: String): ShareToken! + shareTokenRequiresPassword(token: String!): Boolean! search(query: String!, limitPhotos: Int, limitAlbums: Int): SearchResult! } @@ -1752,6 +1767,20 @@ func (ec *executionContext) field_Query_search_args(ctx context.Context, rawArgs return args, nil } +func (ec *executionContext) field_Query_shareTokenRequiresPassword_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["token"]; ok { + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["token"] = arg0 + return args, nil +} + func (ec *executionContext) field_Query_shareToken_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -4325,6 +4354,47 @@ func (ec *executionContext) _Query_shareToken(ctx context.Context, field graphql return ec.marshalNShareToken2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐShareToken(ctx, field.Selections, res) } +func (ec *executionContext) _Query_shareTokenRequiresPassword(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Query", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Query_shareTokenRequiresPassword_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().ShareTokenRequiresPassword(rctx, args["token"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + func (ec *executionContext) _Query_search(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -6880,6 +6950,20 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } return res }) + case "shareTokenRequiresPassword": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_shareTokenRequiresPassword(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) case "search": field := field out.Concurrently(i, func() (res graphql.Marshaler) { diff --git a/api/graphql/resolvers/share_token.go b/api/graphql/resolvers/share_token.go index e06e3566..cd9ff0fc 100644 --- a/api/graphql/resolvers/share_token.go +++ b/api/graphql/resolvers/share_token.go @@ -84,6 +84,21 @@ func (r *queryResolver) ShareToken(ctx context.Context, tokenValue string, passw return token, nil } +func (r *queryResolver) ShareTokenRequiresPassword(ctx context.Context, tokenValue string) (bool, error) { + row := r.Database.QueryRow("SELECT * FROM share_token WHERE value = ?", tokenValue) + token, err := models.NewShareTokenFromRow(row) + if err != nil { + if err == sql.ErrNoRows { + return false, errors.New("share not found") + } else { + return false, err + } + } + + requiresPassword := token.Password != nil + return requiresPassword, nil +} + func (r *mutationResolver) ShareAlbum(ctx context.Context, albumID int, expire *time.Time, password *string) (*models.ShareToken, error) { user := auth.UserFromContext(ctx) if user == nil { diff --git a/api/graphql/schema.graphql b/api/graphql/schema.graphql index 916e3a3a..25dbc01b 100644 --- a/api/graphql/schema.graphql +++ b/api/graphql/schema.graphql @@ -39,6 +39,7 @@ type Query { photo(id: Int!): Photo! shareToken(token: String!, password: String): ShareToken! + shareTokenRequiresPassword(token: String!): Boolean! search(query: String!, limitPhotos: Int, limitAlbums: Int): SearchResult! } diff --git a/ui/src/Pages/SharePage/SharePage.js b/ui/src/Pages/SharePage/SharePage.js index 2a1be2bd..c02200b8 100644 --- a/ui/src/Pages/SharePage/SharePage.js +++ b/ui/src/Pages/SharePage/SharePage.js @@ -1,14 +1,15 @@ import React from 'react' +import PropTypes from 'prop-types' import RouterProps from 'react-router-prop-types' import { Route, Switch } from 'react-router-dom' import AlbumSharePage from './AlbumSharePage' import PhotoSharePage from './PhotoSharePage' -import { Query } from 'react-apollo' +import { useQuery } from 'react-apollo' import gql from 'graphql-tag' const tokenQuery = gql` - query SharePageToken($token: String!) { - shareToken(token: $token) { + query SharePageToken($token: String!, $password: String) { + shareToken(token: $token, password: $password) { token album { ...AlbumProps @@ -70,30 +71,60 @@ const tokenQuery = gql` } ` +const tokenPasswordProtectedQuery = gql` + query ShareTokenRequiresPassword($token: String!) { + shareTokenRequiresPassword(token: $token) + } +` + +const AuthorizedTokenRoute = ({ match, password }) => { + const { loading, error, data } = useQuery(tokenQuery, { + variables: { token: match.params.token, password }, + }) + + if (error) return error.message + if (loading) return 'Loading...' + + if (data.shareToken.album) { + return + } + + if (data.shareToken.photo) { + return + } + + return

Share not found

+} + +AuthorizedTokenRoute.propTypes = { + match: PropTypes.object.isRequired, + password: PropTypes.string, +} + +const TokenRoute = ({ match }) => { + const { loading, error, data } = useQuery(tokenPasswordProtectedQuery, { + variables: { token: match.params.token }, + }) + + if (error) return error.message + if (loading) return 'Loading...' + + if (data.shareTokenRequiresPassword == true) { + return 'Please provide password' + } + + return +} + +TokenRoute.propTypes = { + match: PropTypes.object.isRequired, +} + const SharePage = ({ match }) => { return ( - {({ match }) => ( - - {({ loading, error, data }) => { - if (error) return error.message - if (loading) return 'Loading...' - - if (data.shareToken.album) { - return ( - - ) - } - - if (data.shareToken.photo) { - return - } - - return

Share not found

- }} -
- )} + {({ match }) => }
Share not found