LinuxMint Cinnamon Desklet setting: Password type - passwords

By developing a Cinnamon Desklet I need to save access to an API that contains a password.
Storing it as "entry" type in settins-schema.json seems to be not so secure.
Does anyone have any common practice/example (eg: via gnome-keyring) how you can store info securely in desklets standard config?
Or does anyone know a Desklet that can be taken as an example?

The best way to store passwords securely in a Desklet is to use the gnome-keyring. This will allow you to store the password in a secure location that is only accessible by the Desklet.

So for those who are trying to save password in Cinnamon Desklet/Applet/Extension to gnome-keyring:
In your (desklet|applet|etc..).js:
...
// Some imports you will need:
const Secret = imports.gi.Secret;
const Clutter = imports.gi.Clutter;
const ModalDialog = imports.ui.modalDialog;
...
class YouClass{
constructor(){
...
this.STORE_SCHEMA = new Secret.Schema("org.myOwn.Schema",Secret.SchemaFlags.NONE,{});
...
}
on_password_stored(source, result) {
Secret.password_store_finish(result);
}
onPasswordSave() {
let dialog = new PasswordDialog (
_("'%s' settings..\nPlease enter password:").format(this._(this._meta.name)),
(password) => {
Secret.password_store(this.STORE_SCHEMA, {}, Secret.COLLECTION_DEFAULT,
"MyFancyPassword", password, null, this.on_password_stored);
}
);
dialog.open();
}
needMyPasswordFunction() {
let password = Secret.password_lookup_sync(this.STORE_SCHEMA, {}, null );
}
}
// --- Define PasswordDialog
class PasswordDialog extends ModalDialog.ModalDialog {
constructor(label, callback){
super();
this.contentLayout.add(new St.Label({ text: label }));
this.callback = callback;
this.entry = new St.Entry({ style: 'background: green; color:yellow;' });
this.entry.clutter_text.set_password_char('\u25cf');
this.contentLayout.add(this.entry);
this.setInitialKeyFocus( this.entry.clutter_text );
this.setButtons([
{
label: "Save",
action: () => {
const pwd = this.entry.get_text();
this.callback( pwd );
this.destroy();
},
key: Clutter.KEY_Return,
focused: false
},
{
label: "Cancel",
action: () => {
this.destroy();
},
key: null,
focused: false
}
]);
}
}
And if you want to link with a config button put this also to settings-schema.json:
"savepassword": {
"type": "button",
"description": "Enter and save password ...",
"callback": "onPasswordSave"
}

Related

Apollo-client: How to add cache fragment in different Query?

when I create feed in Screen A,
I add and in order to apply this change to screen directly I change cache like below.
in this Screen A, I use seeAllFeeds query.
So I add this added feed to seeAllFeeds.
const updateUploadPhoto = (cache, result) => {
const {
data: { createFeed },
} = result;
if (createFeed.id) {
cache.modify({
id: "ROOT_QUERY",
fields: {
seeAllFeeds(prev) {
return [createFeed, ...prev];
},
},
});
}
}
So it works well.
But problem is I use seeCertainUserFeedpoem query in Screen B.
And here, I also need to add this added feed info.
However this screen is not applied unless I refresh the screen.
(Due to flatlist, I can't refresh because if so, scroll goes to top.)
So I add this cache.modify once again below.
I also match the data with seeCertainUserFeedpoem manually and update.
const updateUploadPhoto = (cache, result) => {
const {
data: { createFeed },
} = result;
const FeedpomeId = `Feedpoem:${createFeed.id}`;
const FeedpoemFragment = {
caption: createFeed.caption,
commentNumber: createFeed.commentNumber,
createdAt: createFeed.createdAt,
id: createFeed.id,
isLiked: createFeed.isLiked,
isMine: createFeed.isMine,
likeNumber: createFeed.likeNumber,
photos: createFeed.photos,
poemCaption: null,
poemCommentNumber: 0,
poemLikeNumber: 0,
poemTitle: null,
updatedAt: createFeed.updatedAt,
};
if (createFeed.id) {
cache.modify({
id: "ROOT_QUERY",
fields: {
seeAllFeeds(prev) {
return [createFeed, ...prev];
},
},
});
cache.modify({
id: FeedpomeId,
data: FeedpoemFragment,
});
navigation.navigate("Tabs", { screen: "일상" });
}
};
So I also try this way, not cache.modify but client.writeFragment.
const updateUploadPhoto = (cache, result) => {
const {
data: { createFeed },
} = result;
const FeedpomeId = `Feedpoem:${createFeed.id}`;
const FeedpoemFragment = {
caption: createFeed.caption,
commentNumber: createFeed.commentNumber,
createdAt: createFeed.createdAt,
id: createFeed.id,
isLiked: createFeed.isLiked,
isMine: createFeed.isMine,
likeNumber: createFeed.likeNumber,
photos: createFeed.photos,
poemCaption: null,
poemCommentNumber: 0,
poemLikeNumber: 0,
poemTitle: null,
updatedAt: createFeed.updatedAt,
};
if (createFeed.id) {
cache.modify({
id: "ROOT_QUERY",
fields: {
seeAllFeeds(prev) {
return [createFeed, ...prev];
},
},
});
client.writeFragment({
id: FeedpomeId,
data: FeedpoemFragment,
});
navigation.navigate("Tabs", { screen: "일상" });
}
};
But both dont' work.
And 2nd way throws me this error.
LogBox.js:173 Possible Unhandled Promise Rejection (id: 0): Error:
Cannot read properties of undefined (reading 'definitions') Error:
Cannot read properties of undefined (reading 'definitions')
what is the problem here?
I can edit / delete with cache.
Because I understand that first find the exact cache by id.
However creating is hard.
I want to share my cache as well.
But this is too long.
chat is also welcome, please help me.

Nest JS authorization with CASL doesn't work as expected

EXPECTING:
Be able to get user info with id equal to my id only (which is saved in JWT token).
CURRENT RESULT:
I am able to get info about all users with some id.
Used Nest Js docs while creating this solution.
Do appreciate your help.
/casl-ability.factory.ts
type Subjects = InferSubjects<typeof User | typeof Role | 'User'> | 'all';
export type AppAbility = Ability<[Action, Subjects]>;
export class CaslAbilityFactory {
createForUser(userDataFromJWT: JwtAccessTokenInput) {
const { can, cannot, build } = new AbilityBuilder<
Ability<[Action, Subjects]>
>(Ability as AbilityClass<AppAbility>);
// TESTING THIS CASE
can(Action.Read, User, {
id: userDataFromJWT.sub,
});
return build({
detectSubjectType: (item) =>
item.constructor as ExtractSubjectType<Subjects>,
});
}
private hasRole(roles: unknown[], role: UserRoles): boolean {
return roles.includes(role);
}
}
/getUser.policyHandler.ts
export class GetUserPolicyHandler implements IPolicyHandler {
handle(ability: AppAbility) {
return ability.can(Action.Read, User);
}
}
/types.ts
export enum Action {
Manage = 'manage',
Create = 'create',
Read = 'read',
Update = 'update',
Delete = 'delete',
}
export interface IPolicyHandler {
handle(ability: AppAbility): boolean;
}
type PolicyHandlerCallback = (ability: AppAbility) => boolean;
export type PolicyHandler = IPolicyHandler | PolicyHandlerCallback;
/policies.guard.ts
#Injectable()
export class PoliciesGuard implements CanActivate {
constructor(
private reflector: Reflector,
private caslAbilityFactory: CaslAbilityFactory,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const policyHandlers =
this.reflector.get<PolicyHandler[]>(
CHECK_POLICIES_KEY,
context.getHandler(),
) || [];
const ctx = GqlExecutionContext.create(context);
const { user }: { user: JwtAccessTokenInput } = ctx.getContext().req;
const ability = this.caslAbilityFactory.createForUser(user);
return policyHandlers.every((handler) =>
this.execPolicyHandler(handler, ability),
);
}
private execPolicyHandler(handler: PolicyHandler, ability: AppAbility) {
if (typeof handler === 'function') {
return handler(ability);
}
return handler.handle(ability);
}
}
user.resolver.ts
#Resolver(() => User)
export class UserResolver {
constructor(private readonly userService: UserService) {}
#Query(() => User, { name: 'user' })
#UseGuards(PoliciesGuard)
#CheckPolicies(new GetUserPolicyHandler())
#UseInterceptors(UserNotExistsByIDInterceptor)
async findOne(#Args('id', { type: () => Int }) id: number): Promise<User> {
return await this.userService.findOne(id);
}
}
possible duplicate of NestJS + CASL + Mongoose: CASL cannot infer subject type from Mongoose Schema
if you're using mongoose you need to inject the model to allow InferSubjects to retrieve the type thus allowing you to use filters and fields.

Zapier Dynamic Custom Fields

I am trying to offer custom fields from a platform as input fields I have done this in the past with another platform and with Zapiers older UI. It does not seem to be that simple now.
const options = {
url: 'https://edapi.campaigner.com/v1/Database',
method: 'GET',
headers: {
'X-API-KEY': bundle.authData.ApiKey
},
params: {
'ApiKey': bundle.authData.ApiKey
}
};
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
const col = results.DatabaseColumns.filter((item) => item.IsCustom).map((item) => {
return {
...item,
id: item["ColumnName"],
};
});
return col});
That is what I am trying to use for the Action. I am using this same does for a Trigger and it works there, but not as Dynamic Field Option along with other standard inputs.
Not sure if I need to tweak the code or if I can invoke the data that the Trigger would pull?
Here is the visual of the fields, but I need it to pull and offer the custom fields. This would be like favorite color, etc.
Image of Zap
Any help is appreciated.
I was able to use this code:
// Configure a request to an endpoint of your api that
// returns custom field meta data for the authenticated
// user. Don't forget to congigure authentication!
const options = {
url: 'https://edapi.campaigner.com/v1/Database',
method: 'GET',
headers: {
'X-API-KEY': bundle.authData.ApiKey
},
params: {
'ApiKey': bundle.authData.ApiKey
}
};
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
var col = results.DatabaseColumns.filter((item) => item.IsCustom).map((item) => {
return {
...item,
key: item["ColumnName"],
value: item["ColumnName"]
};
});
//var col = col.filter(items => ['FirstName', 'LastName'].indexOf(items) >= 0 )
for (var i = col.length; i--;) {
if (col[i].key === 'FirstName' || col[i].key === 'LastName' ) {
col.splice(i, 1);
}
}
return col});
/*
return [
{
"key": "FirstName",
"value":"First Name"
},
{
"key": "LastName",
"value": "Last Name"
},
{
"key": "Test",
"value": "Test 2"
},
*/

Vue.js - Element UI - HTML message in MessageBox

I'm using vue-js 2.3 and element-ui. This question is more specific to the MessageBox component for which you can find the documentation here
Problem
I'd like to be able to enter html message in the MessageBox
More specifically I would like to display the data contained in dataForMessage by using a v-for loop.
Apparently, we can insert vnode in the message but I have no idea where to find some information about the syntax.
https://jsfiddle.net/7ugahcfz/
var Main = {
data:function () {
return {
dataForMessage: [
{
name:'Paul',
gender:'Male',
},
{
name:'Anna',
gender:'Female',
},
],
}
},
methods: {
open() {
const h = this.$createElement;
this.$msgbox({
title: 'Message',
message: h('p', null, [
h('span', null, 'Message can be '),
h('i', { style: 'color: teal' }, 'VNode '),
h('span', null, 'but I would like to see the data from '),
h('i', { style: 'color: teal' }, 'dataForMessage'),
])
}).then(action => {
});
},
}
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
I think this is what you want.
methods: {
open() {
const h = this.$createElement;
let people = this.dataForMessage.map(p => h('li', `${p.name} ${p.gender}`))
const message = h('div', null, [
h('h1', "Model wished"),
h('div', "The data contained in dataForMessage are:"),
h('ul', people)
])
this.$msgbox({
title: 'Message',
message
}).then(action => {
});
},
}
Example.
You can also use html directly and convert to vnodes by using domProps:
const html = '<div><h1>Model wished</h1><div>The data contained in dataForMessage are:</div><ul><li>Paul Male</li><li>Anna Female</li></ul></div>'
const message = h("div", {domProps:{innerHTML: html}})
(The above is simplified without the loop. Just to get the idea)
Fiddle

Bind validation results from ajax request to form model in mithril

Hi I would like to bind html inputs with validation response model returned from API like that:
{"userName":[{"memberNames":["UserName"],"errorMessage":"Field User Name is required."}],"acceptTerms":[{"memberNames":["AcceptTerms"],"errorMessage":"Accepting terms is requried"}]}
And my component in mithril
var RegisterPage = {
vm: {
userName: m.prop(),
password: m.prop(),
confirmPassword: m.prop(),
acceptTerms: m.prop(false)
},
controller: function (args) {
this.title = 'Register new user account';
this.vm = RegisterPage.vm;
this.register = function (e) {
e.preventDefault();
apiRequest({ method: "POST", url: "http://localhost:12116/auth/register", data: RegisterPage.vm }).then(RegisterPage.vm.registerResult)
}
},
view: function (ctrl, args) {
return m('form.input-group',
[
m('.input-row', [m('label', 'Email'), m('input[type=email][placeholder=Your email address like myemail#email.com]', { onchange: m.withAttr("value", ctrl.vm.email) })]),
m('.input-row', [m('label', 'Password'), m('input[type=password][placeholder=your password]', { onchange: m.withAttr("value", ctrl.vm.password) })]),
m('.input-row', [m('label', 'Confirm password'), m('input[type=password][placeholder=your password]', { onchange: m.withAttr("value", ctrl.vm.confirmPassword) })]),
m('.input-row', [m('label', 'Accept terms and conditions'), m('input[type=checkbox]', { onchange: m.withAttr("checked", ctrl.vm.acceptTerms) })]),
m('button[type=submit].btn btn-positive btn-block', { onclick: ctrl.register }, 'Register account')
]);
}
}
I am looking for some generic solution. I would like to mark invalid fields with css class and add field validation message.
UPDATE
In my project I use some wrapper around m.request to get more details when 400 is thrown
function apiRequest(args) {
NProgress.start();
if (!args.unwrapError) {
args.unwrapError = function (data, xhr) {
if (xhr.status === 401)
{
layout.badRequestMsg(xhr.statusText);
}
NProgress.done();
return data;
}
}
if (!args.unwrapSuccess) {
args.unwrapSuccess = function (data, xhr) {
NProgress.done();
return data;
}
}
return m.request(args);
}