├── src
├── main
│ ├── webapp
│ │ ├── META-INF
│ │ │ └── MANIFEST.MF
│ │ ├── WEB-INF
│ │ │ ├── cron.xml
│ │ │ ├── appengine-web.xml
│ │ │ └── web.xml
│ │ ├── index.html
│ │ └── js
│ │ │ ├── statistic.js
│ │ │ └── jquery-1.6.2.min.js
│ ├── java
│ │ └── com
│ │ │ └── pangratz
│ │ │ └── memorablepw
│ │ │ ├── rest
│ │ │ ├── TweetDePasswordResource.java
│ │ │ ├── TweetEnPasswordResource.java
│ │ │ ├── MemorablePwApplication.java
│ │ │ ├── UpdatePasswordCountersResource.java
│ │ │ ├── MemorablePwServerResource.java
│ │ │ ├── AddPasswordsResource.java
│ │ │ ├── AbstractTweetPasswordResource.java
│ │ │ └── StatisticResource.java
│ │ │ ├── model
│ │ │ ├── PMF.java
│ │ │ ├── Configuration.java
│ │ │ ├── Statistic.java
│ │ │ ├── PasswordCounter.java
│ │ │ ├── Password.java
│ │ │ └── ModelUtils.java
│ │ │ └── util
│ │ │ ├── AddPasswordUtil.java
│ │ │ └── TweetDateUtil.java
│ └── resources
│ │ ├── META-INF
│ │ ├── persistence.xml
│ │ └── jdoconfig.xml
│ │ ├── logging.properties
│ │ └── log4j.properties
└── test
│ └── java
│ └── com
│ └── pangratz
│ └── memorablepw
│ ├── util
│ ├── GetAccessToken.java
│ └── TweetDateUtilTest.java
│ └── model
│ └── ModelUtilsTest.java
├── get_password.applescript
├── fillUpStorage.sh
├── .gitignore
├── get_lang.applescript
├── tweet_passwords.js
├── README.md
├── commit_passwords.js
└── pom.xml
/src/main/webapp/META-INF/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Class-Path:
--------------------------------------------------------------------------------
/get_password.applescript:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pangratz/memorablepw/master/get_password.applescript
--------------------------------------------------------------------------------
/fillUpStorage.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | COUNT=1500
3 | STEP=100
4 | CURRENT=$COUNT
5 | while true; do
6 | node commit_passwords.js fill $CURRENT
7 | let CURRENT=$CURRENT+$STEP
8 | done
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Password Assistant.app
2 | node_modules
3 | src/main/resources/twitter4j.properties
4 | src/main/resources/memorablepw*
5 |
6 | .classpath
7 | .settings
8 | .project
9 |
10 | target
--------------------------------------------------------------------------------
/src/main/java/com/pangratz/memorablepw/rest/TweetDePasswordResource.java:
--------------------------------------------------------------------------------
1 | package com.pangratz.memorablepw.rest;
2 |
3 | public class TweetDePasswordResource extends AbstractTweetPasswordResource {
4 |
5 | @Override
6 | protected String getLang() {
7 | return "de";
8 | }
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/pangratz/memorablepw/rest/TweetEnPasswordResource.java:
--------------------------------------------------------------------------------
1 | package com.pangratz.memorablepw.rest;
2 |
3 |
4 | public class TweetEnPasswordResource extends AbstractTweetPasswordResource {
5 |
6 | @Override
7 | protected String getLang() {
8 | return "en";
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/cron.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | /tweetEnPassword
4 | Tweet a new english password every 10 minutes
5 | every 10 minutes synchronized
6 |
7 |
8 | /tweetDePassword
9 | Tweet a new german password every 10 minutes
10 | every 10 minutes synchronized
11 |
12 |
--------------------------------------------------------------------------------
/src/main/java/com/pangratz/memorablepw/model/PMF.java:
--------------------------------------------------------------------------------
1 | package com.pangratz.memorablepw.model;
2 |
3 | import javax.jdo.JDOHelper;
4 | import javax.jdo.PersistenceManagerFactory;
5 |
6 | public final class PMF {
7 | private static final PersistenceManagerFactory pmfInstance = JDOHelper
8 | .getPersistenceManagerFactory("transactions-optional");
9 |
10 | public static PersistenceManagerFactory get() {
11 | return pmfInstance;
12 | }
13 |
14 | private PMF() {
15 | }
16 | }
--------------------------------------------------------------------------------
/src/main/webapp/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | memorablepw - Mister Password
5 |
6 |
7 |
8 |
9 |
10 |
11 | loading ...
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/appengine-web.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | memorablepw
7 | 1
8 |
9 |
10 |
11 |
12 |
13 | true
14 |
15 |
16 |
--------------------------------------------------------------------------------
/get_lang.applescript:
--------------------------------------------------------------------------------
1 | -- a standard split function
2 | on split of aString by sep
3 | local aList, delims
4 | tell AppleScript
5 | set delims to text item delimiters
6 | set text item delimiters to sep
7 | set aList to text items of aString
8 | set text item delimiters to delims
9 | end tell
10 | return aList
11 | end split
12 |
13 |
14 | -- pipe the output of defaults through a few more commands
15 | set cmd to "defaults read NSGlobalDomain AppleLanguages | awk '{gsub(/[^a-zA-Z-]/,\"\");print}' | grep -v '^$'"
16 | set langs to do shell script cmd
17 |
18 | -- get the first item in the list
19 | set lang to item 1 of (split of langs by return)
20 | return lang
--------------------------------------------------------------------------------
/src/main/java/com/pangratz/memorablepw/rest/MemorablePwApplication.java:
--------------------------------------------------------------------------------
1 | package com.pangratz.memorablepw.rest;
2 |
3 | import org.restlet.Application;
4 | import org.restlet.Restlet;
5 | import org.restlet.routing.Router;
6 |
7 | public class MemorablePwApplication extends Application {
8 |
9 | @Override
10 | public Restlet createInboundRoot() {
11 | Router router = new Router(getContext());
12 | router.attach("/password", AddPasswordsResource.class);
13 | router.attach("/statistic", StatisticResource.class);
14 | router.attach("/tweetEnPassword", TweetEnPasswordResource.class);
15 | router.attach("/tweetDePassword", TweetDePasswordResource.class);
16 | router.attach("/updatePasswordCounters", UpdatePasswordCountersResource.class);
17 | return router;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/pangratz/memorablepw/rest/UpdatePasswordCountersResource.java:
--------------------------------------------------------------------------------
1 | package com.pangratz.memorablepw.rest;
2 |
3 | import org.restlet.representation.Representation;
4 | import org.restlet.resource.ResourceException;
5 |
6 | import com.pangratz.memorablepw.model.ModelUtils;
7 |
8 | public class UpdatePasswordCountersResource extends MemorablePwServerResource {
9 |
10 | private ModelUtils mModelUtils;
11 |
12 | @Override
13 | protected void doInit() throws ResourceException {
14 | super.doInit();
15 |
16 | mModelUtils = ModelUtils.getInstance();
17 | }
18 |
19 | @Override
20 | protected Representation get() throws ResourceException {
21 | mModelUtils.updatePasswordCounters();
22 | return createSuccessRepresentation("updated password counters");
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/pangratz/memorablepw/model/Configuration.java:
--------------------------------------------------------------------------------
1 | package com.pangratz.memorablepw.model;
2 |
3 | import javax.jdo.annotations.PersistenceCapable;
4 | import javax.jdo.annotations.Persistent;
5 | import javax.jdo.annotations.PrimaryKey;
6 |
7 | @PersistenceCapable
8 | public class Configuration {
9 |
10 | @Persistent
11 | private int nextPasswordLength;
12 |
13 | @PrimaryKey
14 | @Persistent
15 | private String lang;
16 |
17 | public String getLang() {
18 | return lang;
19 | }
20 |
21 | public int getNextPasswordLength() {
22 | return nextPasswordLength;
23 | }
24 |
25 | public void setLang(String lang) {
26 | this.lang = lang;
27 | }
28 |
29 | public void setNextPasswordLength(int nextPasswordLength) {
30 | this.nextPasswordLength = nextPasswordLength;
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/persistence.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/jdoconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | RestletServlet
9 | org.restlet.ext.servlet.ServerServlet
10 |
11 | org.restlet.application
12 | com.pangratz.memorablepw.rest.MemorablePwApplication
13 |
14 |
15 |
16 |
17 |
18 | RestletServlet
19 | /*
20 |
21 |
22 |
23 | index.html
24 |
25 |
--------------------------------------------------------------------------------
/src/main/java/com/pangratz/memorablepw/model/Statistic.java:
--------------------------------------------------------------------------------
1 | package com.pangratz.memorablepw.model;
2 |
3 | public class Statistic {
4 |
5 | // 31 - 8 + 1 = 24
6 | private int[] counts = new int[24];
7 | private int overallPasswordsCount;
8 | private final String lang;
9 |
10 | public Statistic(String lang) {
11 | super();
12 | this.lang = lang;
13 | }
14 |
15 | public int getIndex(int length) {
16 | // project length to an interval between 0 <= index <= 23
17 | return Math.min(counts.length - 1, Math.max(length - 8, 0));
18 | }
19 |
20 | public String getLang() {
21 | return lang;
22 | }
23 |
24 | public int getOverallPasswordsCount() {
25 | return overallPasswordsCount;
26 | }
27 |
28 | public int getPasswordsCount(int length) {
29 | return counts[getIndex(length)];
30 | }
31 |
32 | public void setOverallPasswordsCount(int overallPasswordsCount) {
33 | this.overallPasswordsCount = overallPasswordsCount;
34 | }
35 |
36 | public void setPasswordCount(int length, int count) {
37 | counts[getIndex(length)] = count;
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/resources/logging.properties:
--------------------------------------------------------------------------------
1 | # A default java.util.logging configuration.
2 | # (All App Engine logging is through java.util.logging by default).
3 | #
4 | # To use this configuration, copy it into your application's WEB-INF
5 | # folder and add the following to your appengine-web.xml:
6 | #
7 | #
8 | #
9 | #
10 | #
11 |
12 | # Set the default logging level for all loggers to WARNING
13 | .level = WARNING
14 |
15 | # Set the default logging level for ORM, specifically, to WARNING
16 | DataNucleus.JDO.level=WARNING
17 | DataNucleus.Persistence.level=WARNING
18 | DataNucleus.Cache.level=WARNING
19 | DataNucleus.MetaData.level=WARNING
20 | DataNucleus.General.level=WARNING
21 | DataNucleus.Utility.level=WARNING
22 | DataNucleus.Transaction.level=WARNING
23 | DataNucleus.Datastore.level=WARNING
24 | DataNucleus.ClassLoading.level=WARNING
25 | DataNucleus.Plugin.level=WARNING
26 | DataNucleus.ValueGeneration.level=WARNING
27 | DataNucleus.Enhancer.level=WARNING
28 | DataNucleus.SchemaTool.level=WARNING
29 |
--------------------------------------------------------------------------------
/src/main/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | # A default log4j configuration for log4j users.
2 | #
3 | # To use this configuration, deploy it into your application's WEB-INF/classes
4 | # directory. You are also encouraged to edit it as you like.
5 |
6 | # Configure the console as our one appender
7 | log4j.appender.A1=org.apache.log4j.ConsoleAppender
8 | log4j.appender.A1.layout=org.apache.log4j.PatternLayout
9 | log4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] - %m%n
10 |
11 | # tighten logging on the DataNucleus Categories
12 | log4j.category.DataNucleus.JDO=WARN, A1
13 | log4j.category.DataNucleus.Persistence=WARN, A1
14 | log4j.category.DataNucleus.Cache=WARN, A1
15 | log4j.category.DataNucleus.MetaData=WARN, A1
16 | log4j.category.DataNucleus.General=WARN, A1
17 | log4j.category.DataNucleus.Utility=WARN, A1
18 | log4j.category.DataNucleus.Transaction=WARN, A1
19 | log4j.category.DataNucleus.Datastore=WARN, A1
20 | log4j.category.DataNucleus.ClassLoading=WARN, A1
21 | log4j.category.DataNucleus.Plugin=WARN, A1
22 | log4j.category.DataNucleus.ValueGeneration=WARN, A1
23 | log4j.category.DataNucleus.Enhancer=WARN, A1
24 | log4j.category.DataNucleus.SchemaTool=WARN, A1
25 |
--------------------------------------------------------------------------------
/src/main/webapp/js/statistic.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 |
3 | $.getJSON('/statistic', function(statisticData) {
4 |
5 | var i = 0;
6 | var datasets = [];
7 | for (i in statisticData) {
8 | var statistic = statisticData[i];
9 | var lang = statistic['lang'];
10 |
11 | $('#overallPasswordsCount').append(statistic.overallPasswordsCount + ' ' + lang + ' passwords');
12 |
13 | var lastTweetDateHtml = 'no more ' + lang +' passwords to tweet :(';
14 | if (statistic.overallPasswordsCount > 0) {
15 | var days = statistic.period.days;
16 | var hours = statistic.period.hours;
17 | var minutes = statistic.period.minutes;
18 | var periodStr = days + (days == 1 ? ' day ' : ' days ');
19 | periodStr += hours + (hours == 1 ? ' hour ' : ' hours ');
20 | periodStr += minutes + (minutes== 1 ? ' minute' : ' minutes');
21 | lastTweetDateHtml = periodStr + ' until the end of ' + lang + ' passwords';
22 | }
23 | $('#lastTweetPeriod').append(lastTweetDateHtml);
24 |
25 | datasets[i] = {
26 | label: lang,
27 | data: statistic.passwords.map(function(item) {
28 | return [item.length, item.count];
29 | })
30 | };
31 | }
32 |
33 | $.plot($('#graph'), datasets);
34 | });
35 |
36 | });
--------------------------------------------------------------------------------
/tweet_passwords.js:
--------------------------------------------------------------------------------
1 | var applescript = require('applescript');
2 | var Twitter = require('twitter');
3 | var fs = require("fs");
4 |
5 | var props = JSON.parse(fs.readFileSync('twitter_properties.json', 'utf8'));
6 | var twitter = new Twitter(props);
7 |
8 | var executeAppleScript = function(pwLength){
9 | pwLength = pwLength || 18;
10 | pwLength = Math.min(Math.max(8, pwLength), 31);
11 | applescript.execFile('get_password.applescript', [pwLength], function(err, rtn) {
12 | if (err) {
13 | // Something went wrong!
14 | }
15 | console.log('length: ' + pwLength + ' password = ' + rtn);
16 | if (rtn) {
17 | twitter.updateStatus(rtn, function(data){
18 | });
19 | }
20 |
21 | setTimeout(function(){
22 | executeAppleScript(pwLength < 31 ? pwLength + 1 : 8);
23 | }, 1000*60*10); // invoke in 10 minutes
24 | });
25 | };
26 |
27 | // get latest tweet character count
28 | var params = {
29 | count: 1
30 | };
31 | twitter.getUserTimeline(params, function(json){
32 | if (Array.isArray(json) && json.length > 0) {
33 | var tweet = json[0];
34 | var length = tweet.text.length + 1;
35 | console.log('start generating passwords of length ' + length);
36 | executeAppleScript(length);
37 | } else {
38 | console.log('unable to get latest tweet');
39 | console.log(json);
40 | }
41 | });
--------------------------------------------------------------------------------
/src/main/java/com/pangratz/memorablepw/model/PasswordCounter.java:
--------------------------------------------------------------------------------
1 | package com.pangratz.memorablepw.model;
2 |
3 | import javax.jdo.annotations.IdGeneratorStrategy;
4 | import javax.jdo.annotations.PersistenceCapable;
5 | import javax.jdo.annotations.Persistent;
6 | import javax.jdo.annotations.PrimaryKey;
7 |
8 | import com.google.appengine.api.datastore.Key;
9 |
10 | @PersistenceCapable
11 | public class PasswordCounter {
12 |
13 | @PrimaryKey
14 | @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
15 | private Key key;
16 |
17 | @Persistent
18 | private String lang;
19 |
20 | @Persistent
21 | private long count;
22 |
23 | @Persistent
24 | private int length;
25 |
26 | public void decrement() {
27 | this.count--;
28 | }
29 |
30 | public long getCount() {
31 | return count;
32 | }
33 |
34 | public Key getKey() {
35 | return key;
36 | }
37 |
38 | public String getLang() {
39 | return lang;
40 | }
41 |
42 | public int getLength() {
43 | return length;
44 | }
45 |
46 | public boolean increment(long inc) {
47 | this.count += inc;
48 | return (inc > 0);
49 | }
50 |
51 | public void setCount(long count) {
52 | this.count = count;
53 | }
54 |
55 | public void setKey(Key key) {
56 | this.key = key;
57 | }
58 |
59 | public void setLang(String lang) {
60 | this.lang = lang;
61 | }
62 |
63 | public void setLength(int length) {
64 | this.length = length;
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/com/pangratz/memorablepw/rest/MemorablePwServerResource.java:
--------------------------------------------------------------------------------
1 | package com.pangratz.memorablepw.rest;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import org.restlet.data.Status;
7 | import org.restlet.ext.json.JsonRepresentation;
8 | import org.restlet.representation.Representation;
9 | import org.restlet.resource.ResourceException;
10 | import org.restlet.resource.ServerResource;
11 |
12 | import com.pangratz.memorablepw.model.ModelUtils;
13 |
14 | public abstract class MemorablePwServerResource extends ServerResource {
15 |
16 | protected ModelUtils mModelUtils;
17 |
18 | protected Representation createErrorRepresentation(String string) {
19 | Map